├── README.md ├── composer.json └── src ├── Connector └── ElFinderConnector.php ├── Driver ├── ElFinderVolumeDriver.php ├── ElFinderVolumeDropbox.php ├── ElFinderVolumeFTP.php ├── ElFinderVolumeFTPIIIS.php ├── ElFinderVolumeFlysystem.php ├── ElFinderVolumeLocalFileSystem.php ├── ElFinderVolumeMySQL.php └── ElFinderVolumeS3.php ├── ElFinder.php ├── Plugins ├── AutoResize │ └── plugin.php ├── Normalizer │ └── plugin.php ├── Sanitizer │ └── plugin.php ├── Thumbnails │ └── plugin.php └── Watermark │ ├── logo.png │ └── plugin.php ├── Utility ├── GetUrl.php └── HasDir.php └── mime.types /README.md: -------------------------------------------------------------------------------- 1 | ElFinderPHP 2 | =========== 3 | 4 | ElFinderPHP is PHP 5.4 modification of server side ElFinder. 5 | 6 | Based on [@nao-pon](https://github.com/nao-pon/) [fork of ElFinder] (https://github.com/nao-pon/elFinder/) 7 | 8 | Provides support for the following backend: 9 | 10 | 1. LocalVolumeDriver 11 | 2. Dropbox driver 12 | 3. FTP Driver 13 | 4. S3 Driver 14 | 5. MySql driver 15 | 6. Flysystem support (via modified @barryvdh [driver](https://github.com/barryvdh/elfinder-flysystem-driver)) 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helios-ag/fm-elfinder-php-connector", 3 | "description": "ElFinder PHP backend, 5.4 compliant", 4 | "keywords": ["elfinder", "filemanager"], 5 | "homepage": "http://github.com/helios-ag/ElFinderPHP", 6 | "type": "library", 7 | "license": "BSD-3-Clause", 8 | "authors": [ 9 | { 10 | "name": "Al Ganiev", 11 | "email": "helios.ag@gmail.com" 12 | }, 13 | { 14 | "name": "ElFinder authors", 15 | "homepage": "https://github.com/Studio-42/elFinder" 16 | }, 17 | { 18 | "name": "Symfony Community", 19 | "homepage": "https://github.com/helios-ag/ElFinderPHP/contributors" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=5.4", 24 | "intervention/image": "^2.3" 25 | }, 26 | "suggest": { 27 | "dropbox-php/dropbox-php": "Allows you to use Dropbox storage", 28 | "aws/aws-sdk-php": "Allows you to use AWS S3 storage" 29 | }, 30 | "autoload": { 31 | "psr-4": {"FM\\ElFinderPHP\\": "src/"} 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Connector/ElFinderConnector.php: -------------------------------------------------------------------------------- 1 | elFinder = $elFinder; 46 | if ($debug) { 47 | $this->header = 'Content-Type: text/html; charset=utf-8'; 48 | } 49 | } 50 | 51 | /** 52 | * Execute elFinder command and output result 53 | * 54 | * @param array $queryParameters (optional) Provided query parameters or auto guessed via $_GET. 55 | * @return void 56 | * @author Nicolas MURE 57 | **/ 58 | public function run($queryParameters = null) { 59 | if ($queryParameters === null) { 60 | $queryParameters = $_GET; 61 | } 62 | exit(json_encode($this->execute($queryParameters))); 63 | } 64 | 65 | /** 66 | * Execute elFinder command and returns result 67 | * @param array $queryParameters GET query parameters. 68 | * @return array 69 | * @author Dmitry (dio) Levashov 70 | **/ 71 | public function execute($queryParameters) { 72 | $isPost = $_SERVER["REQUEST_METHOD"] == 'POST'; 73 | $src = $_SERVER["REQUEST_METHOD"] == 'POST' ? array_merge($_POST, $queryParameters) : $queryParameters; 74 | if ($isPost && !$src && $rawPostData = @file_get_contents('php://input')) { 75 | // for support IE XDomainRequest() 76 | $parts = explode('&', $rawPostData); 77 | foreach($parts as $part) { 78 | list($key, $value) = array_pad(explode('=', $part), 2, ''); 79 | $src[$key] = rawurldecode($value); 80 | } 81 | $_POST = $src; 82 | $_REQUEST = array_merge_recursive($src, $_REQUEST); 83 | } 84 | $cmd = isset($src['cmd']) ? $src['cmd'] : ''; 85 | $args = array(); 86 | 87 | if (!function_exists('json_encode')) { 88 | $error = $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_JSON); 89 | return $this->output(array('error' => '{"error":["'.implode('","', $error).'"]}', 'raw' => true)); 90 | } 91 | 92 | if (!$this->elFinder->loaded()) { 93 | return $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_VOL), 'debug' => $this->elFinder->mountErrors)); 94 | } 95 | 96 | // telepat_mode: on 97 | if (!$cmd && $isPost) { 98 | return $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UPLOAD, elFinder::ERROR_UPLOAD_TOTAL_SIZE), 'header' => 'Content-Type: text/html')); 99 | } 100 | // telepat_mode: off 101 | 102 | if (!$this->elFinder->commandExists($cmd)) { 103 | return $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD))); 104 | } 105 | 106 | // collect required arguments to exec command 107 | foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) { 108 | $arg = $name == 'FILES' 109 | ? $_FILES 110 | : (isset($src[$name]) ? $src[$name] : ''); 111 | 112 | if (!is_array($arg)) { 113 | $arg = trim($arg); 114 | } 115 | if ($req && (!isset($arg) || $arg === '')) { 116 | return $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd))); 117 | } 118 | $args[$name] = $arg; 119 | } 120 | 121 | $args['debug'] = isset($src['debug']) ? !!$src['debug'] : false; 122 | 123 | return $this->output($this->elFinder->exec($cmd, $this->input_filter($args))); 124 | } 125 | 126 | /** 127 | * Output data to array 128 | * 129 | * @param array data to output 130 | * @return array 131 | * @author Dmitry (dio) Levashov 132 | **/ 133 | protected function output(array $data) { 134 | $header = isset($data['header']) ? $data['header'] : $this->header; 135 | unset($data['header']); 136 | if ($header) { 137 | if (is_array($header)) { 138 | foreach ($header as $h) { 139 | header($h); 140 | } 141 | } else { 142 | header($header); 143 | } 144 | } 145 | 146 | if (isset($data['pointer'])) { 147 | // The output buffer might be dirty which causes broken file downloads. 148 | ob_end_clean(); 149 | rewind($data['pointer']); 150 | fpassthru($data['pointer']); 151 | if (!empty($data['volume'])) { 152 | $data['volume']->close($data['pointer'], $data['info']['hash']); 153 | } 154 | return array(); 155 | } else { 156 | if (!empty($data['raw']) && !empty($data['error'])) { 157 | return array('error' => $data['error']); 158 | } else { 159 | return $data; 160 | } 161 | } 162 | 163 | } 164 | 165 | /** 166 | * Remove null & stripslashes applies on "magic_quotes_gpc" 167 | * 168 | * @param mixed $args 169 | * @return mixed 170 | * @author Naoki Sawada 171 | */ 172 | private function input_filter($args) { 173 | static $magic_quotes_gpc = NULL; 174 | 175 | if ($magic_quotes_gpc === NULL) 176 | $magic_quotes_gpc = (version_compare(PHP_VERSION, '5.4', '<') && get_magic_quotes_gpc()); 177 | 178 | if (is_array($args)) { 179 | return array_map(array(& $this, 'input_filter'), $args); 180 | } 181 | $res = str_replace("\0", '', $args); 182 | $magic_quotes_gpc && ($res = stripslashes($res)); 183 | return $res; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Driver/ElFinderVolumeFTP.php: -------------------------------------------------------------------------------- 1 | '0', 'r' => '4', 'w' => '2', 'x' => '1'); 9 | $chmod = substr(strtr($chmod, $trans), 1); 10 | $array = str_split($chmod, 3); 11 | return array_sum(str_split($array[0])) . array_sum(str_split($array[1])) . array_sum(str_split($array[2])); 12 | } 13 | 14 | ElFinder::$netDrivers['ftp'] = 'FTP'; 15 | 16 | /** 17 | * Simple ElFinder driver for FTP 18 | * 19 | * @author Dmitry (dio) Levashov 20 | * @author Cem (discofever) 21 | **/ 22 | class ElFinderVolumeFTP extends ElFinderVolumeDriver { 23 | 24 | /** 25 | * Driver id 26 | * Must be started from letter and contains [a-z0-9] 27 | * Used as part of volume id 28 | * 29 | * @var string 30 | **/ 31 | protected $driverId = 'f'; 32 | 33 | /** 34 | * FTP Connection Instance 35 | * 36 | * @var ftp 37 | **/ 38 | protected $connect = null; 39 | 40 | /** 41 | * Directory for tmp files 42 | * If not set driver will try to use tmbDir as tmpDir 43 | * 44 | * @var string 45 | **/ 46 | protected $tmpPath = ''; 47 | 48 | /** 49 | * Last FTP error message 50 | * 51 | * @var string 52 | **/ 53 | protected $ftpError = ''; 54 | 55 | /** 56 | * FTP server output list as ftp on linux 57 | * 58 | * @var bool 59 | **/ 60 | protected $ftpOsUnix; 61 | 62 | /** 63 | * Tmp folder path 64 | * 65 | * @var string 66 | **/ 67 | protected $tmp = ''; 68 | 69 | /** 70 | * Constructor 71 | * Extend options with required fields 72 | * 73 | * @return \FM\ElFinderPHP\Driver\ElFinderVolumeFTP 74 | * @author Dmitry (dio) Levashov 75 | * @author Cem (DiscoFever) 76 | */ 77 | public function __construct() { 78 | $opts = array( 79 | 'host' => 'localhost', 80 | 'user' => '', 81 | 'pass' => '', 82 | 'port' => 21, 83 | 'mode' => 'passive', 84 | 'path' => '/', 85 | 'timeout' => 20, 86 | 'owner' => true, 87 | 'tmbPath' => '', 88 | 'tmpPath' => '', 89 | 'dirMode' => 0755, 90 | 'fileMode' => 0644 91 | ); 92 | $this->options = array_merge($this->options, $opts); 93 | $this->options['mimeDetect'] = 'internal'; 94 | } 95 | 96 | /*********************************************************************/ 97 | /* INIT AND CONFIGURE */ 98 | /*********************************************************************/ 99 | 100 | /** 101 | * Prepare FTP connection 102 | * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn 103 | * 104 | * @return bool 105 | * @author Dmitry (dio) Levashov 106 | * @author Cem (DiscoFever) 107 | **/ 108 | protected function init() { 109 | if (!$this->options['host'] 110 | || !$this->options['user'] 111 | || !$this->options['pass'] 112 | || !$this->options['port']) { 113 | return $this->setError('Required options undefined.'); 114 | } 115 | 116 | if (!function_exists('ftp_connect')) { 117 | return $this->setError('FTP extension not loaded.'); 118 | } 119 | 120 | // remove protocol from host 121 | $scheme = parse_url($this->options['host'], PHP_URL_SCHEME); 122 | 123 | if ($scheme) { 124 | $this->options['host'] = substr($this->options['host'], strlen($scheme)+3); 125 | } 126 | 127 | // normalize root path 128 | $this->root = $this->options['path'] = $this->_normpath($this->options['path']); 129 | 130 | if (empty($this->options['alias'])) { 131 | $this->options['alias'] = $this->options['user'].'@'.$this->options['host']; 132 | // $num = ElFinder::$volumesCnt-1; 133 | // $this->options['alias'] = $this->root == '/' || $this->root == '.' ? 'FTP folder '.$num : basename($this->root); 134 | } 135 | 136 | $this->rootName = $this->options['alias']; 137 | $this->options['separator'] = '/'; 138 | 139 | return $this->connect(); 140 | 141 | } 142 | 143 | 144 | /** 145 | * Configure after successfull mount. 146 | * 147 | * @return void 148 | * @author Dmitry (dio) Levashov 149 | **/ 150 | protected function configure() { 151 | parent::configure(); 152 | 153 | if (!empty($this->options['tmpPath'])) { 154 | if ((is_dir($this->options['tmpPath']) || @mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) { 155 | $this->tmp = $this->options['tmpPath']; 156 | } 157 | } 158 | 159 | if (!$this->tmp && $this->tmbPath) { 160 | $this->tmp = $this->tmbPath; 161 | } 162 | 163 | if (!$this->tmp) { 164 | $this->disabled[] = 'mkfile'; 165 | $this->disabled[] = 'paste'; 166 | $this->disabled[] = 'duplicate'; 167 | $this->disabled[] = 'upload'; 168 | $this->disabled[] = 'edit'; 169 | $this->disabled[] = 'archive'; 170 | $this->disabled[] = 'extract'; 171 | } 172 | 173 | // echo $this->tmp; 174 | 175 | } 176 | 177 | /** 178 | * Connect to ftp server 179 | * 180 | * @return bool 181 | * @author Dmitry (dio) Levashov 182 | **/ 183 | protected function connect() { 184 | if (!($this->connect = ftp_connect($this->options['host'], $this->options['port'], $this->options['timeout']))) { 185 | return $this->setError('Unable to connect to FTP server '.$this->options['host']); 186 | } 187 | if (!ftp_login($this->connect, $this->options['user'], $this->options['pass'])) { 188 | $this->umount(); 189 | return $this->setError('Unable to login into '.$this->options['host']); 190 | } 191 | 192 | // switch off extended passive mode - may be usefull for some servers 193 | @ftp_exec($this->connect, 'epsv4 off' ); 194 | // enter passive mode if required 195 | ftp_pasv($this->connect, $this->options['mode'] == 'passive'); 196 | 197 | // enter root folder 198 | if (!ftp_chdir($this->connect, $this->root) 199 | || $this->root != ftp_pwd($this->connect)) { 200 | $this->umount(); 201 | return $this->setError('Unable to open root folder.'); 202 | } 203 | 204 | // check for MLST support 205 | $features = ftp_raw($this->connect, 'FEAT'); 206 | if (!is_array($features)) { 207 | $this->umount(); 208 | return $this->setError('Server does not support command FEAT.'); 209 | } 210 | 211 | foreach ($features as $feat) { 212 | if (strpos(trim($feat), 'MLST') === 0) { 213 | return true; 214 | } 215 | } 216 | 217 | return $this->setError('Server does not support command MLST.'); 218 | } 219 | 220 | /*********************************************************************/ 221 | /* FS API */ 222 | /*********************************************************************/ 223 | 224 | /** 225 | * Close opened connection 226 | * 227 | * @return void 228 | * @author Dmitry (dio) Levashov 229 | **/ 230 | public function umount() { 231 | $this->connect && @ftp_close($this->connect); 232 | } 233 | 234 | 235 | /** 236 | * Parse line from ftp_rawlist() output and return file stat (array) 237 | * 238 | * @param string $raw line from ftp_rawlist() output 239 | * @return array 240 | * @author Dmitry Levashov 241 | **/ 242 | protected function parseRaw($raw) { 243 | $info = preg_split("/\s+/", $raw, 9); 244 | $stat = array(); 245 | 246 | if (count($info) < 9 || $info[8] == '.' || $info[8] == '..') { 247 | return false; 248 | } 249 | 250 | if (!isset($this->ftpOsUnix)) { 251 | $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); 252 | } 253 | 254 | if ($this->ftpOsUnix) { 255 | 256 | $stat['ts'] = strtotime($info[5].' '.$info[6].' '.$info[7]); 257 | if (empty($stat['ts'])) { 258 | $stat['ts'] = strtotime($info[6].' '.$info[5].' '.$info[7]); 259 | } 260 | 261 | $name = $info[8]; 262 | 263 | if (preg_match('|(.+)\-\>(.+)|', $name, $m)) { 264 | $name = trim($m[1]); 265 | $target = trim($m[2]); 266 | if (substr($target, 0, 1) != '/') { 267 | $target = $this->root.'/'.$target; 268 | } 269 | $target = $this->_normpath($target); 270 | $stat['name'] = $name; 271 | if ($this->_inpath($target, $this->root) 272 | && ($tstat = $this->stat($target))) { 273 | $stat['size'] = $tstat['mime'] == 'directory' ? 0 : $info[4]; 274 | $stat['alias'] = $this->_relpath($target); 275 | $stat['thash'] = $tstat['hash']; 276 | $stat['mime'] = $tstat['mime']; 277 | $stat['read'] = $tstat['read']; 278 | $stat['write'] = $tstat['write']; 279 | } else { 280 | 281 | $stat['mime'] = 'symlink-broken'; 282 | $stat['read'] = false; 283 | $stat['write'] = false; 284 | $stat['size'] = 0; 285 | 286 | } 287 | return $stat; 288 | } 289 | 290 | $perm = $this->parsePermissions($info[0]); 291 | $stat['name'] = $name; 292 | $stat['mime'] = substr(strtolower($info[0]), 0, 1) == 'd' ? 'directory' : $this->mimetype($stat['name']); 293 | $stat['size'] = $stat['mime'] == 'directory' ? 0 : $info[4]; 294 | $stat['read'] = $perm['read']; 295 | $stat['write'] = $perm['write']; 296 | $stat['perm'] = substr($info[0], 1); 297 | } else { 298 | die('Windows ftp servers not supported yet'); 299 | } 300 | 301 | return $stat; 302 | } 303 | 304 | /** 305 | * Parse permissions string. Return array(read => true/false, write => true/false) 306 | * 307 | * @param string $perm permissions string 308 | * @return string 309 | * @author Dmitry (dio) Levashov 310 | **/ 311 | protected function parsePermissions($perm) { 312 | $res = array(); 313 | $parts = array(); 314 | $owner = $this->options['owner']; 315 | for ($i = 0, $l = strlen($perm); $i < $l; $i++) { 316 | $parts[] = substr($perm, $i, 1); 317 | } 318 | 319 | $read = ($owner && $parts[0] == 'r') || $parts[4] == 'r' || $parts[7] == 'r'; 320 | 321 | return array( 322 | 'read' => $parts[0] == 'd' ? $read && (($owner && $parts[3] == 'x') || $parts[6] == 'x' || $parts[9] == 'x') : $read, 323 | 'write' => ($owner && $parts[2] == 'w') || $parts[5] == 'w' || $parts[8] == 'w' 324 | ); 325 | } 326 | 327 | /** 328 | * Cache dir contents 329 | * 330 | * @param string $path dir path 331 | * @return void 332 | * @author Dmitry Levashov 333 | **/ 334 | protected function cacheDir($path) { 335 | $this->dirsCache[$path] = array(); 336 | 337 | if (preg_match('/\'|\"/', $path)) { 338 | foreach (ftp_nlist($this->connect, $path) as $p) { 339 | if (($stat = $this->_stat($p)) &&empty($stat['hidden'])) { 340 | // $files[] = $stat; 341 | $this->dirsCache[$path][] = $p; 342 | } 343 | } 344 | return; 345 | } 346 | foreach (ftp_rawlist($this->connect, $path) as $raw) { 347 | if (($stat = $this->parseRaw($raw))) { 348 | $p = $path.'/'.$stat['name']; 349 | $stat = $this->updateCache($p, $stat); 350 | if (empty($stat['hidden'])) { 351 | // $files[] = $stat; 352 | $this->dirsCache[$path][] = $p; 353 | } 354 | } 355 | } 356 | } 357 | 358 | /** 359 | * Return ftp transfer mode for file 360 | * 361 | * @param string $path file path 362 | * @return string 363 | * @author Dmitry (dio) Levashov 364 | **/ 365 | protected function ftpMode($path) { 366 | return strpos($this->mimetype($path), 'text/') === 0 ? FTP_ASCII : FTP_BINARY; 367 | } 368 | 369 | /*********************** paths/urls *************************/ 370 | 371 | /** 372 | * Return parent directory path 373 | * 374 | * @param string $path file path 375 | * @return string 376 | * @author Dmitry (dio) Levashov 377 | **/ 378 | protected function _dirname($path) { 379 | return dirname($path); 380 | } 381 | 382 | /** 383 | * Return file name 384 | * 385 | * @param string $path file path 386 | * @return string 387 | * @author Dmitry (dio) Levashov 388 | **/ 389 | protected function _basename($path) { 390 | return basename($path); 391 | } 392 | 393 | /** 394 | * Join dir name and file name and retur full path 395 | * 396 | * @param string $dir 397 | * @param string $name 398 | * @return string 399 | * @author Dmitry (dio) Levashov 400 | **/ 401 | protected function _joinPath($dir, $name) { 402 | return $dir.DIRECTORY_SEPARATOR.$name; 403 | } 404 | 405 | /** 406 | * Return normalized path, this works the same as os.path.normpath() in Python 407 | * 408 | * @param string $path path 409 | * @return string 410 | * @author Troex Nevelin 411 | **/ 412 | protected function _normpath($path) { 413 | if (empty($path)) { 414 | $path = '.'; 415 | } 416 | // path must be start with / 417 | $path = preg_replace('|^\.\/?|', '/', $path); 418 | $path = preg_replace('/^([^\/])/', "/$1", $path); 419 | 420 | if (strpos($path, '/') === 0) { 421 | $initial_slashes = true; 422 | } else { 423 | $initial_slashes = false; 424 | } 425 | 426 | if (($initial_slashes) 427 | && (strpos($path, '//') === 0) 428 | && (strpos($path, '///') === false)) { 429 | $initial_slashes = 2; 430 | } 431 | 432 | $initial_slashes = (int) $initial_slashes; 433 | 434 | $comps = explode('/', $path); 435 | $new_comps = array(); 436 | foreach ($comps as $comp) { 437 | if (in_array($comp, array('', '.'))) { 438 | continue; 439 | } 440 | 441 | if (($comp != '..') 442 | || (!$initial_slashes && !$new_comps) 443 | || ($new_comps && (end($new_comps) == '..'))) { 444 | array_push($new_comps, $comp); 445 | } elseif ($new_comps) { 446 | array_pop($new_comps); 447 | } 448 | } 449 | $comps = $new_comps; 450 | $path = implode('/', $comps); 451 | if ($initial_slashes) { 452 | $path = str_repeat('/', $initial_slashes) . $path; 453 | } 454 | 455 | return $path ? $path : '.'; 456 | } 457 | 458 | /** 459 | * Return file path related to root dir 460 | * 461 | * @param string $path file path 462 | * @return string 463 | * @author Dmitry (dio) Levashov 464 | **/ 465 | protected function _relpath($path) { 466 | return $path == $this->root ? '' : substr($path, strlen($this->root)+1); 467 | } 468 | 469 | /** 470 | * Convert path related to root dir into real path 471 | * 472 | * @param string $path file path 473 | * @return string 474 | * @author Dmitry (dio) Levashov 475 | **/ 476 | protected function _abspath($path) { 477 | return $path == $this->separator ? $this->root : $this->root.$this->separator.$path; 478 | } 479 | 480 | /** 481 | * Return fake path started from root dir 482 | * 483 | * @param string $path file path 484 | * @return string 485 | * @author Dmitry (dio) Levashov 486 | **/ 487 | protected function _path($path) { 488 | return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path)); 489 | } 490 | 491 | /** 492 | * Return true if $path is children of $parent 493 | * 494 | * @param string $path path to check 495 | * @param string $parent parent path 496 | * @return bool 497 | * @author Dmitry (dio) Levashov 498 | **/ 499 | protected function _inpath($path, $parent) { 500 | return $path == $parent || strpos($path, $parent.'/') === 0; 501 | } 502 | 503 | /***************** file stat ********************/ 504 | /** 505 | * Return stat for given path. 506 | * Stat contains following fields: 507 | * - (int) size file size in b. required 508 | * - (int) ts file modification time in unix time. required 509 | * - (string) mime mimetype. required for folders, others - optionally 510 | * - (bool) read read permissions. required 511 | * - (bool) write write permissions. required 512 | * - (bool) locked is object locked. optionally 513 | * - (bool) hidden is object hidden. optionally 514 | * - (string) alias for symlinks - link target path relative to root path. optionally 515 | * - (string) target for symlinks - link target path. optionally 516 | * 517 | * If file does not exists - returns empty array or false. 518 | * 519 | * @param string $path file path 520 | * @return array|false 521 | * @author Dmitry (dio) Levashov 522 | **/ 523 | protected function _stat($path) { 524 | $raw = ftp_raw($this->connect, 'MLST '.$path); 525 | 526 | if (is_array($raw) && count($raw) > 1 && substr(trim($raw[0]), 0, 1) == 2) { 527 | $parts = explode(';', trim($raw[1])); 528 | array_pop($parts); 529 | $parts = array_map('strtolower', $parts); 530 | $stat = array(); 531 | // debug($parts); 532 | foreach ($parts as $part) { 533 | 534 | list($key, $val) = explode('=', $part); 535 | 536 | switch ($key) { 537 | case 'type': 538 | $stat['mime'] = strpos($val, 'dir') !== false ? 'directory' : $this->mimetype($path); 539 | break; 540 | 541 | case 'size': 542 | $stat['size'] = $val; 543 | break; 544 | 545 | case 'modify': 546 | $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)); 547 | $stat['ts'] = $ts; 548 | // $stat['date'] = $this->formatDate($ts); 549 | break; 550 | 551 | case 'unix.mode': 552 | $stat['chmod'] = $val; 553 | break; 554 | 555 | case 'perm': 556 | $val = strtolower($val); 557 | $stat['read'] = (int)preg_match('/e|l|r/', $val); 558 | $stat['write'] = (int)preg_match('/w|m|c/', $val); 559 | if (!preg_match('/f|d/', $val)) { 560 | $stat['locked'] = 1; 561 | } 562 | break; 563 | } 564 | } 565 | if (empty($stat['mime'])) { 566 | return array(); 567 | } 568 | if ($stat['mime'] == 'directory') { 569 | $stat['size'] = 0; 570 | } 571 | 572 | if (isset($stat['chmod'])) { 573 | $stat['perm'] = ''; 574 | if ($stat['chmod'][0] == 0) { 575 | $stat['chmod'] = substr($stat['chmod'], 1); 576 | } 577 | 578 | for ($i = 0; $i <= 2; $i++) { 579 | $perm[$i] = array(false, false, false); 580 | $n = isset($stat['chmod'][$i]) ? $stat['chmod'][$i] : 0; 581 | 582 | if ($n - 4 >= 0) { 583 | $perm[$i][0] = true; 584 | $n = $n - 4; 585 | $stat['perm'] .= 'r'; 586 | } else { 587 | $stat['perm'] .= '-'; 588 | } 589 | 590 | if ($n - 2 >= 0) { 591 | $perm[$i][1] = true; 592 | $n = $n - 2; 593 | $stat['perm'] .= 'w'; 594 | } else { 595 | $stat['perm'] .= '-'; 596 | } 597 | 598 | if ($n - 1 == 0) { 599 | $perm[$i][2] = true; 600 | $stat['perm'] .= 'x'; 601 | } else { 602 | $stat['perm'] .= '-'; 603 | } 604 | 605 | $stat['perm'] .= ' '; 606 | } 607 | 608 | $stat['perm'] = trim($stat['perm']); 609 | 610 | $owner = $this->options['owner']; 611 | $read = ($owner && $perm[0][0]) || $perm[1][0] || $perm[2][0]; 612 | 613 | $stat['read'] = $stat['mime'] == 'directory' ? $read && (($owner && $perm[0][2]) || $perm[1][2] || $perm[2][2]) : $read; 614 | $stat['write'] = ($owner && $perm[0][1]) || $perm[1][1] || $perm[2][1]; 615 | unset($stat['chmod']); 616 | 617 | } 618 | 619 | return $stat; 620 | 621 | } 622 | 623 | return array(); 624 | } 625 | 626 | /** 627 | * Return true if path is dir and has at least one childs directory 628 | * 629 | * @param string $path dir path 630 | * @return bool 631 | * @author Dmitry (dio) Levashov 632 | **/ 633 | protected function _subdirs($path) { 634 | 635 | if (preg_match('/\s|\'|\"/', $path)) { 636 | foreach (ftp_nlist($this->connect, $path) as $p) { 637 | if (($stat = $this->stat($path.'/'.$p)) && $stat['mime'] == 'directory') { 638 | return true; 639 | } 640 | } 641 | return false; 642 | } 643 | 644 | foreach (ftp_rawlist($this->connect, $path) as $str) { 645 | if (($stat = $this->parseRaw($str)) && $stat['mime'] == 'directory') { 646 | return true; 647 | } 648 | } 649 | return false; 650 | } 651 | 652 | /** 653 | * Return object width and height 654 | * Ususaly used for images, but can be realize for video etc... 655 | * 656 | * @param string $path file path 657 | * @param string $mime file mime type 658 | * @return string 659 | * @author Dmitry (dio) Levashov 660 | **/ 661 | protected function _dimensions($path, $mime) { 662 | return false; 663 | } 664 | 665 | /******************** file/dir content *********************/ 666 | 667 | /** 668 | * Return files list in directory. 669 | * 670 | * @param string $path dir path 671 | * @return array 672 | * @author Dmitry (dio) Levashov 673 | * @author Cem (DiscoFever) 674 | **/ 675 | protected function _scandir($path) { 676 | $files = array(); 677 | 678 | foreach (ftp_rawlist($this->connect, $path) as $str) { 679 | if (($stat = $this->parseRaw($str))) { 680 | $files[] = $path.DIRECTORY_SEPARATOR.$stat['name']; 681 | } 682 | } 683 | 684 | return $files; 685 | } 686 | 687 | /** 688 | * Open file and return file pointer 689 | * 690 | * @param string $path file path 691 | * @param bool $write open file for writing 692 | * @return resource|false 693 | * @author Dmitry (dio) Levashov 694 | **/ 695 | protected function _fopen($path, $mode='rb') { 696 | 697 | if ($this->tmp) { 698 | $local = $this->tmp.DIRECTORY_SEPARATOR.md5($path); 699 | 700 | if (ftp_get($this->connect, $local, $path, FTP_BINARY)) { 701 | return @fopen($local, $mode); 702 | } 703 | } 704 | 705 | return false; 706 | } 707 | 708 | /** 709 | * Close opened file 710 | * 711 | * @param resource $fp file pointer 712 | * @return bool 713 | * @author Dmitry (dio) Levashov 714 | **/ 715 | protected function _fclose($fp, $path='') { 716 | @fclose($fp); 717 | if ($path) { 718 | @unlink($this->tmp.DIRECTORY_SEPARATOR.md5($path)); 719 | } 720 | } 721 | 722 | /******************** file/dir manipulations *************************/ 723 | 724 | /** 725 | * Create dir and return created dir path or false on failed 726 | * 727 | * @param string $path parent dir path 728 | * @param string $name new directory name 729 | * @return string|bool 730 | * @author Dmitry (dio) Levashov 731 | **/ 732 | protected function _mkdir($path, $name) { 733 | $path = $path.'/'.$name; 734 | if (ftp_mkdir($this->connect, $path) === false) { 735 | return false; 736 | } 737 | 738 | $this->options['dirMode'] && @ftp_chmod($this->connect, $this->options['dirMode'], $path); 739 | return $path; 740 | } 741 | 742 | /** 743 | * Create file and return it's path or false on failed 744 | * 745 | * @param string $path parent dir path 746 | * @param string $name new file name 747 | * @return string|bool 748 | * @author Dmitry (dio) Levashov 749 | **/ 750 | protected function _mkfile($path, $name) { 751 | if ($this->tmp) { 752 | $path = $path.'/'.$name; 753 | $local = $this->tmp.DIRECTORY_SEPARATOR.md5($path); 754 | $res = touch($local) && ftp_put($this->connect, $path, $local, FTP_ASCII); 755 | @unlink($local); 756 | return $res ? $path : false; 757 | } 758 | return false; 759 | } 760 | 761 | /** 762 | * Create symlink. FTP driver does not support symlinks. 763 | * 764 | * @param string $target link target 765 | * @param string $path symlink path 766 | * @return bool 767 | * @author Dmitry (dio) Levashov 768 | **/ 769 | protected function _symlink($target, $path, $name) { 770 | return false; 771 | } 772 | 773 | /** 774 | * Copy file into another file 775 | * 776 | * @param string $source source file path 777 | * @param string $targetDir target directory path 778 | * @param string $name new file name 779 | * @return bool 780 | * @author Dmitry (dio) Levashov 781 | **/ 782 | protected function _copy($source, $targetDir, $name) { 783 | $res = false; 784 | 785 | if ($this->tmp) { 786 | $local = $this->tmp.DIRECTORY_SEPARATOR.md5($source); 787 | $target = $targetDir.DIRECTORY_SEPARATOR.$name; 788 | 789 | if (ftp_get($this->connect, $local, $source, FTP_BINARY) 790 | && ftp_put($this->connect, $target, $local, $this->ftpMode($target))) { 791 | $res = $target; 792 | } 793 | @unlink($local); 794 | } 795 | 796 | return $res; 797 | } 798 | 799 | /** 800 | * Move file into another parent dir. 801 | * Return new file path or false. 802 | * 803 | * @param string $source source file path 804 | * @param string $target target dir path 805 | * @param string $name file name 806 | * @return string|bool 807 | * @author Dmitry (dio) Levashov 808 | **/ 809 | protected function _move($source, $targetDir, $name) { 810 | $target = $targetDir.DIRECTORY_SEPARATOR.$name; 811 | return ftp_rename($this->connect, $source, $target) ? $target : false; 812 | } 813 | 814 | /** 815 | * Remove file 816 | * 817 | * @param string $path file path 818 | * @return bool 819 | * @author Dmitry (dio) Levashov 820 | **/ 821 | protected function _unlink($path) { 822 | return ftp_delete($this->connect, $path); 823 | } 824 | 825 | /** 826 | * Remove dir 827 | * 828 | * @param string $path dir path 829 | * @return bool 830 | * @author Dmitry (dio) Levashov 831 | **/ 832 | protected function _rmdir($path) { 833 | return ftp_rmdir($this->connect, $path); 834 | } 835 | 836 | /** 837 | * Create new file and write into it from file pointer. 838 | * Return new file path or false on error. 839 | * 840 | * @param resource $fp file pointer 841 | * @param string $dir target dir path 842 | * @param string $name file name 843 | * @param array $stat file stat (required by some virtual fs) 844 | * @return bool|string 845 | * @author Dmitry (dio) Levashov 846 | **/ 847 | protected function _save($fp, $dir, $name, $stat) { 848 | $path = $dir.'/'.$name; 849 | return ftp_fput($this->connect, $path, $fp, $this->ftpMode($path)) 850 | ? $path 851 | : false; 852 | } 853 | 854 | /** 855 | * Get file contents 856 | * 857 | * @param string $path file path 858 | * @return string|false 859 | * @author Dmitry (dio) Levashov 860 | **/ 861 | protected function _getContents($path) { 862 | $contents = ''; 863 | if (($fp = $this->_fopen($path))) { 864 | while (!feof($fp)) { 865 | $contents .= fread($fp, 8192); 866 | } 867 | $this->_fclose($fp, $path); 868 | return $contents; 869 | } 870 | return false; 871 | } 872 | 873 | /** 874 | * Write a string to a file 875 | * 876 | * @param string $path file path 877 | * @param string $content new file content 878 | * @return bool 879 | * @author Dmitry (dio) Levashov 880 | **/ 881 | protected function _filePutContents($path, $content) { 882 | $res = false; 883 | 884 | if ($this->tmp) { 885 | $local = $this->tmp.DIRECTORY_SEPARATOR.md5($path).'.txt'; 886 | 887 | if (@file_put_contents($local, $content, LOCK_EX) !== false 888 | && ($fp = @fopen($local, 'rb'))) { 889 | clearstatcache(); 890 | $res = ftp_fput($this->connect, $path, $fp, $this->ftpMode($path)); 891 | @fclose($fp); 892 | } 893 | file_exists($local) && @unlink($local); 894 | } 895 | 896 | return $res; 897 | } 898 | 899 | /** 900 | * Detect available archivers 901 | * 902 | * @return void 903 | **/ 904 | protected function _checkArchivers() { 905 | // die('Not yet implemented. (_checkArchivers)'); 906 | return array(); 907 | } 908 | 909 | /** 910 | * chmod availability 911 | * 912 | * @return bool 913 | **/ 914 | protected function _chmod($path, $mode) { 915 | $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o",$mode)); 916 | $ret = @chmod($path, $modeOct); 917 | $ret && clearstatcache(); 918 | return $ret; 919 | } 920 | 921 | /** 922 | * Unpack archive 923 | * 924 | * @param string $path archive path 925 | * @param array $arc archiver command and arguments (same as in $this->archivers) 926 | * @return true 927 | * @return void 928 | * @author Dmitry (dio) Levashov 929 | * @author Alexey Sukhotin 930 | **/ 931 | protected function _unpack($path, $arc) { 932 | die('Not yet implemented. (_unpack)'); 933 | return false; 934 | } 935 | 936 | /** 937 | * Recursive symlinks search 938 | * 939 | * @param string $path file/dir path 940 | * @return bool 941 | * @author Dmitry (dio) Levashov 942 | **/ 943 | protected function _findSymlinks($path) { 944 | die('Not yet implemented. (_findSymlinks)'); 945 | if (is_link($path)) { 946 | return true; 947 | } 948 | if (is_dir($path)) { 949 | foreach (scandir($path) as $name) { 950 | if ($name != '.' && $name != '..') { 951 | $p = $path.DIRECTORY_SEPARATOR.$name; 952 | if (is_link($p)) { 953 | return true; 954 | } 955 | if (is_dir($p) && $this->_findSymlinks($p)) { 956 | return true; 957 | } elseif (is_file($p)) { 958 | $this->archiveSize += filesize($p); 959 | } 960 | } 961 | } 962 | } else { 963 | $this->archiveSize += filesize($path); 964 | } 965 | 966 | return false; 967 | } 968 | 969 | /** 970 | * Extract files from archive 971 | * 972 | * @param string $path archive path 973 | * @param array $arc archiver command and arguments (same as in $this->archivers) 974 | * @return true 975 | * @author Dmitry (dio) Levashov, 976 | * @author Alexey Sukhotin 977 | **/ 978 | protected function _extract($path, $arc) 979 | { 980 | // get current directory 981 | $cwd = getcwd(); 982 | 983 | $tmpDir = $this->tempDir(); 984 | if (!$tmpDir) { 985 | return false; 986 | } 987 | 988 | $basename = $this->_basename($path); 989 | $localPath = $tmpDir . DIRECTORY_SEPARATOR . $basename; 990 | 991 | if (!ftp_get($this->connect, $localPath, $path, FTP_BINARY)) { 992 | //cleanup 993 | $this->deleteDir($tmpDir); 994 | return false; 995 | } 996 | 997 | $remoteDirectory = dirname($path); 998 | chdir($tmpDir); 999 | $command = escapeshellcmd($arc['cmd'] . ' ' . $arc['argc'] . ' "' . $basename . '"'); 1000 | exec($command, $output, $return_value); 1001 | unlink($basename); 1002 | if ($return_value != 0) { 1003 | $this->setError(ElFinder::ERROR_EXTRACT_EXEC, 'Command failed '.escapeshellarg($command)); 1004 | $this->deleteDir($tmpDir); //cleanup 1005 | return false; 1006 | } 1007 | 1008 | $filesToProcess = ElFinderVolumeFTP::listFilesInDirectory($tmpDir, true); 1009 | if(!$filesToProcess) { 1010 | $this->setError(ElFinder::ERROR_EXTRACT_EXEC, $tmpDir." is not a directory"); 1011 | $this->deleteDir($tmpDir); //cleanup 1012 | return false; 1013 | } 1014 | if (count($filesToProcess) > 1) { 1015 | 1016 | // for several files - create new directory 1017 | // create unique name for directory 1018 | $name = basename($path); 1019 | if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) { 1020 | $name = substr($name, 0, strlen($name) - strlen($m[0])); 1021 | } 1022 | 1023 | $test = dirname($path) . DIRECTORY_SEPARATOR . $name; 1024 | if ($this->stat($test)) { 1025 | $name = $this->uniqueName(dirname($path), $name, '-', false); 1026 | } 1027 | 1028 | $newPath = dirname($path) . DIRECTORY_SEPARATOR . $name; 1029 | 1030 | $success = $this->_mkdir(dirname($path), $name); 1031 | foreach ($filesToProcess as $filename) { 1032 | if (!$success) { 1033 | break; 1034 | } 1035 | $targetPath = $newPath . DIRECTORY_SEPARATOR . $filename; 1036 | if (is_dir($filename)) { 1037 | $success = $this->_mkdir($newPath, $filename); 1038 | } else { 1039 | $success = ftp_put($this->connect, $targetPath, $filename, FTP_BINARY); 1040 | } 1041 | } 1042 | unset($filename); 1043 | 1044 | } else { 1045 | $filename = $filesToProcess[0]; 1046 | $newPath = $remoteDirectory . DIRECTORY_SEPARATOR . $filename; 1047 | $success = ftp_put($this->connect, $newPath, $filename, FTP_BINARY); 1048 | } 1049 | 1050 | // return to initial directory 1051 | chdir($cwd); 1052 | 1053 | //cleanup 1054 | if(!$this->deleteDir($tmpDir)) { 1055 | return false; 1056 | } 1057 | 1058 | if (!$success) { 1059 | $this->setError(ElFinder::ERROR_FTP_UPLOAD_FILE, $newPath); 1060 | return false; 1061 | } 1062 | $this->clearcache(); 1063 | return $newPath; 1064 | } 1065 | 1066 | /** 1067 | * Create archive and return its path 1068 | * 1069 | * @param string $dir target dir 1070 | * @param array $files files names list 1071 | * @param string $name archive name 1072 | * @param array $arc archiver options 1073 | * @return string|bool 1074 | * @author Dmitry (dio) Levashov, 1075 | * @author Alexey Sukhotin 1076 | **/ 1077 | protected function _archive($dir, $files, $name, $arc) 1078 | { 1079 | // get current directory 1080 | $cwd = getcwd(); 1081 | 1082 | $tmpDir = $this->tempDir(); 1083 | if (!$tmpDir) { 1084 | return false; 1085 | } 1086 | 1087 | //download data 1088 | if (!$this->ftp_download_files($dir, $files, $tmpDir)) { 1089 | //cleanup 1090 | $this->deleteDir($tmpDir); 1091 | return false; 1092 | } 1093 | 1094 | // go to the temporary directory 1095 | chdir($tmpDir); 1096 | 1097 | // path to local copy of archive 1098 | $path = $tmpDir . DIRECTORY_SEPARATOR . $name; 1099 | 1100 | $file_names_string = ""; 1101 | foreach (scandir($tmpDir) as $filename) { 1102 | if ('.' == $filename) { 1103 | continue; 1104 | } 1105 | if ('..' == $filename) { 1106 | continue; 1107 | } 1108 | $file_names_string = $file_names_string . '"' . $filename . '" '; 1109 | } 1110 | $command = escapeshellcmd($arc['cmd'] . ' ' . $arc['argc'] . ' "' . $name . '" ' . $file_names_string); 1111 | 1112 | exec($command, $output, $return_value); 1113 | if ($return_value != 0) { 1114 | $this->setError(ElFinder::ERROR_ARCHIVE_EXEC, 'Command failed '.escapeshellarg($command)); 1115 | $this->deleteDir($tmpDir); //cleanup 1116 | return false; 1117 | } 1118 | 1119 | $remoteArchiveFile = $dir . DIRECTORY_SEPARATOR . $name; 1120 | 1121 | // upload archive 1122 | if (!ftp_put($this->connect, $remoteArchiveFile, $path, FTP_BINARY)) { 1123 | $this->setError(ElFinder::ERROR_FTP_UPLOAD_FILE, $remoteArchiveFile); 1124 | $this->deleteDir($tmpDir); //cleanup 1125 | return false; 1126 | } 1127 | 1128 | // return to initial work directory 1129 | chdir($cwd); 1130 | 1131 | //cleanup 1132 | if(!$this->deleteDir($tmpDir)) { 1133 | return false; 1134 | } 1135 | 1136 | return $remoteArchiveFile; 1137 | } 1138 | 1139 | /** 1140 | * Create writable temporary directory and return path to it. 1141 | * @return string path to the new temporary directory or false in case of error. 1142 | */ 1143 | private function tempDir() 1144 | { 1145 | $tempPath = tempnam($this->tmp, 'ElFinder'); 1146 | if (!$tempPath) { 1147 | $this->setError(ElFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); 1148 | return false; 1149 | } 1150 | $success = unlink($tempPath); 1151 | if (!$success) { 1152 | $this->setError(ElFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); 1153 | return false; 1154 | } 1155 | $success = mkdir($tempPath, 0755, true); 1156 | if (!$success) { 1157 | $this->setError(ElFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); 1158 | return false; 1159 | } 1160 | return $tempPath; 1161 | } 1162 | 1163 | /** 1164 | * Gets in a single FTP request an array of absolute remote FTP paths of files and 1165 | * folders in $remote_directory omitting symbolic links. 1166 | * @param $remote_directory string remote FTP path to scan for file and folders recursively 1167 | * @return array of elements each of which is an array of two elements: 1168 | * 1172 | */ 1173 | private function ftp_scan_dir($remote_directory) 1174 | { 1175 | $buff = ftp_rawlist($this->connect, $remote_directory, true); 1176 | $next_folder = false; 1177 | $items = array(); 1178 | foreach ($buff as $str) { 1179 | if ('' == $str) { 1180 | $next_folder = true; 1181 | continue; 1182 | } 1183 | if ($next_folder) { 1184 | $remote_directory = preg_replace('/\:/', '', $str); 1185 | $next_folder = false; 1186 | $item = array(); 1187 | $item['path'] = $remote_directory; 1188 | $item['type'] = 'd'; // directory 1189 | $items[] = $item; 1190 | continue; 1191 | } 1192 | $info = preg_split("/\s+/", $str, 9); 1193 | $type = substr($info[0], 0, 1); 1194 | switch ($type) { 1195 | case 'l' : //omit symbolic links 1196 | case 'd' : 1197 | break; 1198 | default: 1199 | $remote_file_path = $remote_directory . DIRECTORY_SEPARATOR . $info[8]; 1200 | $item = array(); 1201 | $item['path'] = $remote_file_path; 1202 | $item['type'] = 'f'; // normal file 1203 | $items[] = $item; 1204 | } 1205 | } 1206 | return $items; 1207 | } 1208 | 1209 | /** 1210 | * Downloads specified files from remote directory 1211 | * if there is a directory among files it is downloaded recursively (omitting symbolic links). 1212 | * @param $remote_directory string remote FTP path to a source directory to download from. 1213 | * @param array $files list of files to download from remote directory. 1214 | * @param $dest_local_directory string destination folder to store downloaded files. 1215 | * @return bool true on success and false on failure. 1216 | */ 1217 | private function ftp_download_files($remote_directory, array $files, $dest_local_directory) 1218 | { 1219 | $contents = $this->ftp_scan_dir($remote_directory); 1220 | if (!isset($contents)) { 1221 | $this->setError(ElFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); 1222 | return false; 1223 | } 1224 | foreach ($contents as $item) { 1225 | $drop = true; 1226 | foreach ($files as $file) { 1227 | if ($remote_directory . DIRECTORY_SEPARATOR . $file == $item['path'] || strstr($item['path'], $remote_directory . DIRECTORY_SEPARATOR . $file . DIRECTORY_SEPARATOR)) { 1228 | $drop = false; 1229 | break; 1230 | } 1231 | } 1232 | if ($drop) continue; 1233 | $relative_path = str_replace($remote_directory, '', $item['path']); 1234 | $local_path = $dest_local_directory . DIRECTORY_SEPARATOR . $relative_path; 1235 | switch ($item['type']) { 1236 | case 'd': 1237 | $success = mkdir($local_path); 1238 | break; 1239 | case 'f': 1240 | $success = ftp_get($this->connect, $local_path, $item['path'], FTP_BINARY); 1241 | break; 1242 | default: 1243 | $success = true; 1244 | } 1245 | if (!$success) { 1246 | $this->setError(ElFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); 1247 | return false; 1248 | } 1249 | } 1250 | return true; 1251 | } 1252 | 1253 | /** 1254 | * Delete local directory recursively. 1255 | * @param $dirPath string to directory to be erased. 1256 | * @return bool true on success and false on failure. 1257 | */ 1258 | private function deleteDir($dirPath) 1259 | { 1260 | if (!is_dir($dirPath)) { 1261 | $success = unlink($dirPath); 1262 | } else { 1263 | $success = true; 1264 | foreach (array_reverse(ElFinderVolumeFTP::listFilesInDirectory($dirPath, false)) as $path) { 1265 | $path = $dirPath . DIRECTORY_SEPARATOR . $path; 1266 | if(is_link($path)) { 1267 | unlink($path); 1268 | } else if (is_dir($path)) { 1269 | $success = rmdir($path); 1270 | } else { 1271 | $success = unlink($path); 1272 | } 1273 | if (!$success) { 1274 | break; 1275 | } 1276 | } 1277 | if($success) { 1278 | $success = rmdir($dirPath); 1279 | } 1280 | } 1281 | if(!$success) { 1282 | $this->setError(ElFinder::ERROR_RM, $dirPath); 1283 | return false; 1284 | } 1285 | return $success; 1286 | } 1287 | 1288 | /** 1289 | * Returns array of strings containing all files and folders in the specified local directory. 1290 | * @param $dir 1291 | * @param string $prefix 1292 | * @internal param string $path path to directory to scan. 1293 | * @return array array of files and folders names relative to the $path 1294 | * or an empty array if the directory $path is empty, 1295 | *
1296 | * false if $path is not a directory or does not exist. 1297 | */ 1298 | private static function listFilesInDirectory($dir, $omitSymlinks, $prefix = '') 1299 | { 1300 | if (!is_dir($dir)) { 1301 | return false; 1302 | } 1303 | $excludes = array(".",".."); 1304 | $result = array(); 1305 | $files = scandir($dir); 1306 | if(!$files) { 1307 | return array(); 1308 | } 1309 | foreach($files as $file) { 1310 | if(!in_array($file, $excludes)) { 1311 | $path = $dir.DIRECTORY_SEPARATOR.$file; 1312 | if(is_link($path)) { 1313 | if($omitSymlinks) { 1314 | continue; 1315 | } else { 1316 | $result[] = $prefix.$file; 1317 | } 1318 | } else if(is_dir($path)) { 1319 | $result[] = $prefix.$file.DIRECTORY_SEPARATOR; 1320 | $subs = ElFinderVolumeFTP::listFilesInDirectory($path, $omitSymlinks, $prefix.$file.DIRECTORY_SEPARATOR); 1321 | if($subs) { 1322 | $result = array_merge($result, $subs); 1323 | } 1324 | 1325 | } else { 1326 | $result[] = $prefix.$file; 1327 | } 1328 | } 1329 | } 1330 | return $result; 1331 | } 1332 | 1333 | /** 1334 | * Resize image 1335 | * @param string $hash 1336 | * @param int $width 1337 | * @param int $height 1338 | * @param int $x 1339 | * @param int $y 1340 | * @param string $mode 1341 | * @param string $bg 1342 | * @param int $degree 1343 | * @return array|bool|false 1344 | */ 1345 | public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = NULL) { 1346 | if ($this->commandDisabled('resize')) { 1347 | return $this->setError(ElFinder::ERROR_PERM_DENIED); 1348 | } 1349 | 1350 | if (($file = $this->file($hash)) == false) { 1351 | return $this->setError(ElFinder::ERROR_FILE_NOT_FOUND); 1352 | } 1353 | 1354 | if (!$file['write'] || !$file['read']) { 1355 | return $this->setError(ElFinder::ERROR_PERM_DENIED); 1356 | } 1357 | 1358 | $path = $this->decode($hash); 1359 | 1360 | $tmpDir = $this->tempDir(); 1361 | if (!$tmpDir) { 1362 | return false; 1363 | } 1364 | 1365 | $local_path = $tmpDir . DIRECTORY_SEPARATOR . basename($path); 1366 | $remote_directory = ftp_pwd($this->connect); 1367 | $success = ftp_get($this->connect, $local_path, $path, FTP_BINARY); 1368 | if (!$success) { 1369 | $this->setError(ElFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); 1370 | return false; 1371 | } 1372 | 1373 | if (!$this->canResize($path, $file)) { 1374 | return $this->setError(ElFinder::ERROR_UNSUPPORT_TYPE); 1375 | } 1376 | 1377 | switch($mode) { 1378 | 1379 | case 'propresize': 1380 | $result = $this->imgResize($work_path, $width, $height, true, true, null, $jpgQuality); 1381 | break; 1382 | 1383 | case 'crop': 1384 | $result = $this->imgCrop($work_path, $width, $height, $x, $y, null, $jpgQuality); 1385 | break; 1386 | 1387 | case 'fitsquare': 1388 | $result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality); 1389 | break; 1390 | 1391 | case 'rotate': 1392 | $result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality); 1393 | break; 1394 | 1395 | default: 1396 | $result = $this->imgResize($work_path, $width, $height, false, true, null, $jpgQuality); 1397 | break; 1398 | } 1399 | 1400 | if ($result) { 1401 | 1402 | // upload to FTP and clear temp local file 1403 | 1404 | if (!ftp_put($this->connect, $path, $local_path, FTP_BINARY)) { 1405 | $this->setError(ElFinder::ERROR_FTP_UPLOAD_FILE, $path); 1406 | $this->deleteDir($tmpDir); //cleanup 1407 | } 1408 | 1409 | $this->clearcache(); 1410 | return $this->stat($path); 1411 | } 1412 | 1413 | $this->setError(ElFinder::ERROR_UNKNOWN); 1414 | return false; 1415 | } 1416 | 1417 | } // END class 1418 | -------------------------------------------------------------------------------- /src/Driver/ElFinderVolumeFTPIIIS.php: -------------------------------------------------------------------------------- 1 | connect = ftp_connect($this->options['host'], $this->options['port'], $this->options['timeout']))) { 21 | return $this->setError('Unable to connect to FTP server '.$this->options['host']); 22 | } 23 | if (!ftp_login($this->connect, $this->options['user'], $this->options['pass'])) { 24 | $this->umount(); 25 | return $this->setError('Unable to login into '.$this->options['host']); 26 | } 27 | 28 | // switch off extended passive mode - may be usefull for some servers 29 | //@ftp_exec($this->connect, 'epsv4 off' ); 30 | // enter passive mode if required 31 | $this->options['mode'] = 'active'; 32 | ftp_pasv($this->connect, $this->options['mode'] == 'passive'); 33 | 34 | // enter root folder 35 | if (!ftp_chdir($this->connect, $this->root)) 36 | { 37 | $this->umount(); 38 | return $this->setError('Unable to open root folder.'); 39 | } 40 | 41 | $stat = array(); 42 | $stat['name'] = $this->root; 43 | $stat['mime'] = 'directory'; 44 | $this->filesCache[$this->root] = $stat; 45 | $this->cacheDir($this->root); 46 | 47 | return true; 48 | } 49 | 50 | /** 51 | * Parse line from ftp_rawlist() output and return file stat (array) 52 | * 53 | * @param string $raw line from ftp_rawlist() output 54 | * @return array 55 | **/ 56 | protected function parseRaw($raw) { 57 | $info = preg_split("/\s+/", $raw, 9); 58 | $stat = array(); 59 | 60 | $stat['name'] = join(" ", array_slice($info, 3, 9)); 61 | $stat['read'] = true; 62 | if ($info[2] == '') 63 | { 64 | $stat['size'] = 0; 65 | $stat['mime'] = 'directory'; 66 | } 67 | else 68 | { 69 | $stat['size'] = $info[2]; 70 | $stat['mime'] = $this->mimetype($stat['name']); 71 | } 72 | 73 | return $stat; 74 | } 75 | 76 | /** 77 | * Cache dir contents 78 | * 79 | * @param string $path dir path 80 | * @return void 81 | **/ 82 | protected function cacheDir($path) { 83 | $this->dirsCache[$path] = array(); 84 | 85 | if (preg_match('/\'|\"/', $path)) { 86 | foreach (ftp_nlist($this->connect, $path) as $p) { 87 | if (($stat = $this->_stat($p)) &&empty($stat['hidden'])) { 88 | // $files[] = $stat; 89 | $this->dirsCache[$path][] = $p; 90 | } 91 | } 92 | return; 93 | } 94 | foreach (ftp_rawlist($this->connect, $path) as $raw) { 95 | if (($stat = $this->parseRaw($raw))) { 96 | $p = $path.DIRECTORY_SEPARATOR.$stat['name']; 97 | // $files[] = $stat; 98 | $this->dirsCache[$path][] = $p; 99 | //$stat['name'] = $p; 100 | $this->filesCache[$p] = $stat; 101 | } 102 | } 103 | } 104 | 105 | protected function _stat($path) { 106 | $stat = array(); 107 | 108 | $stat = $this->filesCache[$path]; 109 | 110 | if (empty($stat)) 111 | { 112 | $this->cacheDir($this->_dirname($path)); 113 | $stat = $this->filesCache[$path]; 114 | 115 | } 116 | 117 | return $stat; 118 | } 119 | 120 | 121 | protected function ftp_scan_dir($remote_directory) 122 | { 123 | $buff = ftp_rawlist($this->connect, $remote_directory, true); 124 | $items = array(); 125 | foreach ($buff as $str) { 126 | $info = preg_split("/\s+/", $str, 9); 127 | $remote_file_path = $remote_directory . DIRECTORY_SEPARATOR . join(" ", array_slice($info, 3, 9)); 128 | $item = array(); 129 | $item['type'] = $info[2] == '' ? 'd' : 'f'; 130 | $item['path'] = $remote_file_path; 131 | $items[] = $item; 132 | 133 | if ($item['type'] == 'd') 134 | $items = array_merge($items, $this->ftp_scan_dir($item['path'])); 135 | } 136 | return $items; 137 | } 138 | } // END class 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/Driver/ElFinderVolumeFlysystem.php: -------------------------------------------------------------------------------- 1 | null, 47 | 'glideURL' => null, 48 | 'glideKey' => null, 49 | 'imageManager' => null, 50 | ); 51 | $this->options = array_merge($this->options, $opts); 52 | } 53 | public function mount(array $opts) 54 | { 55 | // If path is not set, use the root 56 | if (!isset($opts['path']) || $opts['path'] === '') { 57 | $opts['path'] = '/'; 58 | } 59 | return parent::mount($opts); 60 | } 61 | /** 62 | * Find the icon based on the used Adapter 63 | * 64 | * @return string 65 | */ 66 | protected function getIcon() 67 | { 68 | try { 69 | $adapter = $this->fs->getAdapter(); 70 | } catch (\Exception $e) { 71 | $adapter = null; 72 | } 73 | if ($adapter instanceof League\Flysystem\Adapter\AbstractFtpAdapter) { 74 | $icon = 'volume_icon_ftp.png'; 75 | } elseif ($adapter instanceof League\Flysystem\Dropbox\DropboxAdapter) { 76 | $icon = 'volume_icon_dropbox.png'; 77 | } else { 78 | $icon = 'volume_icon_local.png'; 79 | } 80 | $parentUrl = defined('ELFINDER_IMG_PARENT_URL')? (rtrim(ELFINDER_IMG_PARENT_URL, '/').'/') : ''; 81 | return $parentUrl . 'img/' . $icon; 82 | } 83 | /** 84 | * Prepare driver before mount volume. 85 | * Return true if volume is ready. 86 | * 87 | * @return bool 88 | **/ 89 | protected function init() 90 | { 91 | $this->fs = $this->options['filesystem']; 92 | if (!($this->fs instanceof FilesystemInterface)) { 93 | return $this->setError('A filesystem instance is required'); 94 | } 95 | // flysystem cache object instance (cached adapter dose not have method like a `getCache()`. 96 | if (isset($this->options['fscache']) && interface_exists('\League\Flysystem\Cached\CacheInterface', false)) { 97 | if ($this->options['fscache'] instanceof \League\Flysystem\Cached\CacheInterface) { 98 | $this->fscache = $this->options['fscache']; 99 | } 100 | } 101 | $this->fs->addPlugin(new GetUrl()); 102 | $this->fs->addPlugin(new HasDir()); 103 | $this->options['icon'] = $this->options['icon'] ?: $this->getIcon(); 104 | $this->root = $this->options['path']; 105 | if ($this->options['glideURL']) { 106 | $this->urlBuilder = UrlBuilderFactory::create($this->options['glideURL'], $this->options['glideKey']); 107 | } 108 | if ($this->options['imageManager']) { 109 | $this->imageManager = $this->options['imageManager']; 110 | } else { 111 | $this->imageManager = new ImageManager(); 112 | } 113 | return true; 114 | } 115 | /** 116 | * Return parent directory path 117 | * 118 | * @param string $path file path 119 | * @return string 120 | **/ 121 | protected function _dirname($path) 122 | { 123 | return Util::dirname($path) ?: '/'; 124 | } 125 | /** 126 | * Return normalized path 127 | * 128 | * @param string $path path 129 | * @return string 130 | **/ 131 | protected function _normpath($path) 132 | { 133 | return $path; 134 | } 135 | /** 136 | * Check if the directory exists in the parent directory. Needed because not all drives handle directories correctly. 137 | * 138 | * @param string $path path 139 | * @return boolean 140 | **/ 141 | protected function _dirExists($path) 142 | { 143 | $dir = $this->_dirname($path); 144 | $basename = basename($path); 145 | 146 | foreach ($this->fs->listContents($dir) as $meta) { 147 | if ($meta['type'] !== 'file' && $meta['basename'] == $basename) { 148 | return true; 149 | } 150 | } 151 | return false; 152 | } 153 | /** 154 | * Get item path from FS method result, It supports item ID based file system 155 | * 156 | * @param boolean|array $result 157 | * @param string $requestPath 158 | */ 159 | protected function _resultPath($result, $requestPath) 160 | { 161 | if (! is_array($result)) { 162 | if ($this->fscache) { 163 | $this->fscache->flush(); 164 | } 165 | $result = $this->fs->getMetaData($requestPath); 166 | } 167 | $path = $result ? (isset($result['path']) ? $result['path'] : $requestPath) : false; 168 | if ($this->fscache && $path !== $requestPath) { 169 | $this->fscache->storeMiss($requestPath); 170 | } 171 | return $path; 172 | } 173 | /** 174 | * Return stat for given path. 175 | * Stat contains following fields: 176 | * - (int) size file size in b. required 177 | * - (int) ts file modification time in unix time. required 178 | * - (string) mime mimetype. required for folders, others - optionally 179 | * - (bool) read read permissions. required 180 | * - (bool) write write permissions. required 181 | * - (bool) locked is object locked. optionally 182 | * - (bool) hidden is object hidden. optionally 183 | * - (string) alias for symlinks - link target path relative to root path. optionally 184 | * - (string) target for symlinks - link target path. optionally 185 | * 186 | * If file does not exists - returns empty array or false. 187 | * 188 | * @param string $path file path 189 | * @return array|false 190 | **/ 191 | protected function _stat($path) 192 | { 193 | $stat = array( 194 | 'size' => 0, 195 | 'ts' => time(), 196 | 'read' => true, 197 | 'write' => true, 198 | 'locked' => false, 199 | 'hidden' => false, 200 | 'mime' => 'directory', 201 | ); 202 | // If root, just return from above 203 | if ($this->root == $path) { 204 | $stat['name'] = $this->root; 205 | return $stat; 206 | } 207 | // If not exists, return empty 208 | if ( !$this->fs->has($path)) { 209 | 210 | // Check if the parent doesn't have this path 211 | if ($this->_dirExists($path)) { 212 | return $stat; 213 | } 214 | 215 | // Neither a file or directory exist, return empty 216 | return array(); 217 | } 218 | $meta = $this->fs->getMetadata($path); 219 | // Set item filename.extension to `name` if exists 220 | if (isset($meta['filename']) && isset($meta['extension'])) { 221 | $stat['name'] = $meta['filename']; 222 | if ($meta['extension'] !== '') { 223 | $stat['name'] .= '.' . $meta['extension']; 224 | } 225 | } 226 | // Get timestamp/size 227 | $stat['ts'] = isset($meta['timestamp'])? $meta['timestamp'] : $this->fs->getTimestamp($path); 228 | $stat['size'] = isset($meta['size'])? $meta['size'] : $this->fs->getSize($path); 229 | 230 | // Check if file, if so, check mimetype 231 | if ($meta['type'] == 'file') { 232 | $stat['mime'] = isset($meta['mimetype'])? $meta['mimetype'] : $this->fs->getMimetype($path); 233 | $imgMimes = ['image/jpeg', 'image/png', 'image/gif']; 234 | if ($this->urlBuilder && in_array($stat['mime'], $imgMimes)) { 235 | $stat['url'] = $this->urlBuilder->getUrl($path, ['ts' => $stat['ts']]); 236 | $stat['tmb'] = $this->urlBuilder->getUrl($path, [ 237 | 'ts' => $stat['ts'], 238 | 'w' => $this->tmbSize, 239 | 'h' => $this->tmbSize, 240 | 'fit' => $this->options['tmbCrop'] ? 'crop' : 'contain', 241 | ]); 242 | } 243 | } 244 | if (! isset($stat['url']) && $this->fs->getUrl()) { 245 | $stat['url'] = 1; 246 | } 247 | return $stat; 248 | } 249 | /***************** file stat ********************/ 250 | /** 251 | * Return true if path is dir and has at least one childs directory 252 | * 253 | * @param string $path dir path 254 | * @return bool 255 | **/ 256 | protected function _subdirs($path) 257 | { 258 | $ret = false; 259 | if ($this->fs->hasDir()) { 260 | $ret = $this->fs->hasDir($path); 261 | } else { 262 | foreach ($this->fs->listContents($path) as $meta) { 263 | if ($meta['type'] !== 'file') { 264 | $ret = true; 265 | break; 266 | } 267 | } 268 | } 269 | return $ret; 270 | } 271 | /** 272 | * Return object width and height 273 | * Usually used for images, but can be realize for video etc... 274 | * 275 | * @param string $path file path 276 | * @param string $mime file mime type 277 | * @return string 278 | **/ 279 | protected function _dimensions($path, $mime) 280 | { 281 | $ret = false; 282 | if ($imgsize = $this->getImageSize($path, $mime)) { 283 | $ret = $imgsize['dimensions']; 284 | } 285 | return $ret; 286 | } 287 | /******************** file/dir content *********************/ 288 | /** 289 | * Return files list in directory 290 | * 291 | * @param string $path dir path 292 | * @return array 293 | **/ 294 | protected function _scandir($path) 295 | { 296 | $paths = array(); 297 | foreach ($this->fs->listContents($path, false) as $object) { 298 | $paths[] = $object['path']; 299 | } 300 | return $paths; 301 | } 302 | /** 303 | * Open file and return file pointer 304 | * 305 | * @param string $path file path 306 | * @param string $mode 307 | * @return resource|false 308 | **/ 309 | protected function _fopen($path, $mode="rb") 310 | { 311 | return $this->fs->readStream($path); 312 | } 313 | /** 314 | * Close opened file 315 | * 316 | * @param resource $fp file pointer 317 | * @param string $path file path 318 | * @return bool 319 | **/ 320 | protected function _fclose($fp, $path='') 321 | { 322 | return @fclose($fp); 323 | } 324 | /******************** file/dir manipulations *************************/ 325 | /** 326 | * Create dir and return created dir path or false on failed 327 | * 328 | * @param string $path parent dir path 329 | * @param string $name new directory name 330 | * @return string|bool 331 | **/ 332 | protected function _mkdir($path, $name) 333 | { 334 | $path = $this->_joinPath($path, $name); 335 | if ($this->fs->createDir($path) === false) { 336 | return false; 337 | } 338 | return $path; 339 | } 340 | /** 341 | * Create file and return it's path or false on failed 342 | * 343 | * @param string $path parent dir path 344 | * @param string $name new file name 345 | * @return string|bool 346 | **/ 347 | protected function _mkfile($path, $name) 348 | { 349 | $path = $this->_joinPath($path, $name); 350 | return $this->_resultPath($this->fs->write($path, ''), $path); 351 | } 352 | /** 353 | * Copy file into another file 354 | * 355 | * @param string $source source file path 356 | * @param string $target target directory path 357 | * @param string $name new file name 358 | * @return bool 359 | **/ 360 | protected function _copy($source, $target, $name) 361 | { 362 | $result = $this->fs->copy($source, $this->_joinPath($target, $name)); 363 | if ($result && $this->fscache) { 364 | $this->fscache->flush(); 365 | } 366 | return $result; 367 | } 368 | /** 369 | * Move file into another parent dir. 370 | * Return new file path or false. 371 | * 372 | * @param string $source source file path 373 | * @param string $target target dir path 374 | * @param string $name file name 375 | * @return string|bool 376 | **/ 377 | protected function _move($source, $target, $name) 378 | { 379 | $path = $this->_joinPath($target, $name); 380 | return $this->_resultPath($this->fs->rename($source, $path), $path); 381 | } 382 | /** 383 | * Remove file 384 | * 385 | * @param string $path file path 386 | * @return bool 387 | **/ 388 | protected function _unlink($path) 389 | { 390 | return $this->fs->delete($path); 391 | } 392 | /** 393 | * Remove dir 394 | * 395 | * @param string $path dir path 396 | * @return bool 397 | **/ 398 | protected function _rmdir($path) 399 | { 400 | return $this->fs->deleteDir($path); 401 | } 402 | /** 403 | * Create new file and write into it from file pointer. 404 | * Return new file path or false on error. 405 | * 406 | * @param resource $fp file pointer 407 | * @param string $dir target dir path 408 | * @param string $name file name 409 | * @param array $stat file stat (required by some virtual fs) 410 | * @return bool|string 411 | **/ 412 | protected function _save($fp, $dir, $name, $stat) 413 | { 414 | $path = $this->_joinPath($dir, $name); 415 | $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); 416 | $config = []; 417 | if (isset(self::$mimetypes[$ext])) { 418 | $config['mimetype'] = self::$mimetypes[$ext]; 419 | } 420 | return $this->_resultPath($this->fs->putStream($path, $fp, $config), $path); 421 | } 422 | /** 423 | * Get file contents 424 | * 425 | * @param string $path file path 426 | * @return string|false 427 | **/ 428 | protected function _getContents($path) 429 | { 430 | return $this->fs->read($path); 431 | } 432 | /** 433 | * Write a string to a file 434 | * 435 | * @param string $path file path 436 | * @param string $content new file content 437 | * @return bool 438 | **/ 439 | protected function _filePutContents($path, $content) 440 | { 441 | return $this->fs->put($path, $content); 442 | } 443 | /*********************** paths/urls *************************/ 444 | /** 445 | * Return file name 446 | * 447 | * @param string $path file path 448 | * @return string 449 | * @author Dmitry (dio) Levashov 450 | **/ 451 | protected function _basename($path) { 452 | return basename($path); 453 | } 454 | /** 455 | * Join dir name and file name and return full path 456 | * 457 | * @param string $dir 458 | * @param string $name 459 | * @return string 460 | * @author Dmitry (dio) Levashov 461 | **/ 462 | protected function _joinPath($dir, $name) 463 | { 464 | return Util::normalizePath($dir.$this->separator.$name); 465 | } 466 | /** 467 | * Return file path related to root dir 468 | * 469 | * @param string $path file path 470 | * @return string 471 | **/ 472 | protected function _relpath($path) 473 | { 474 | return $path; 475 | } 476 | /** 477 | * Convert path related to root dir into real path 478 | * 479 | * @param string $path file path 480 | * @return string 481 | **/ 482 | protected function _abspath($path) 483 | { 484 | return $path; 485 | } 486 | /** 487 | * Return fake path started from root dir 488 | * 489 | * @param string $path file path 490 | * @return string 491 | **/ 492 | protected function _path($path) 493 | { 494 | return $this->rootName.$this->separator.$path; 495 | } 496 | /** 497 | * Return true if $path is children of $parent 498 | * 499 | * @param string $path path to check 500 | * @param string $parent parent path 501 | * @return bool 502 | * @author Dmitry (dio) Levashov 503 | **/ 504 | protected function _inpath($path, $parent) 505 | { 506 | return $path == $parent || strpos($path, $parent.'/') === 0; 507 | } 508 | /** 509 | * Create symlink 510 | * 511 | * @param string $source file to link to 512 | * @param string $targetDir folder to create link in 513 | * @param string $name symlink name 514 | * @return bool 515 | **/ 516 | protected function _symlink($source, $targetDir, $name) { 517 | return false; 518 | } 519 | /** 520 | * Extract files from archive 521 | * 522 | * @param string $path file path 523 | * @param array $arc archiver options 524 | * @return bool 525 | **/ 526 | protected function _extract($path, $arc) 527 | { 528 | return false; 529 | } 530 | /** 531 | * Create archive and return its path 532 | * 533 | * @param string $dir target dir 534 | * @param array $files files names list 535 | * @param string $name archive name 536 | * @param array $arc archiver options 537 | * @return string|bool 538 | **/ 539 | protected function _archive($dir, $files, $name, $arc) 540 | { 541 | return false; 542 | } 543 | /** 544 | * Detect available archivers 545 | * 546 | * @return void 547 | **/ 548 | protected function _checkArchivers() 549 | { 550 | return; 551 | } 552 | /** 553 | * chmod implementation 554 | * 555 | * @return bool 556 | **/ 557 | protected function _chmod($path, $mode) { 558 | return false; 559 | } 560 | /** 561 | * Resize image 562 | * 563 | * @param string $hash image file 564 | * @param int $width new width 565 | * @param int $height new height 566 | * @param int $x X start poistion for crop 567 | * @param int $y Y start poistion for crop 568 | * @param string $mode action how to mainpulate image 569 | * @param string $bg background color 570 | * @param int $degree rotete degree 571 | * @param int $jpgQuality JEPG quality (1-100) 572 | * @return array|false 573 | * @author Dmitry (dio) Levashov 574 | * @author Alexey Sukhotin 575 | * @author nao-pon 576 | * @author Troex Nevelin 577 | **/ 578 | public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = null) { 579 | if ($this->commandDisabled('resize')) { 580 | return $this->setError(elFinder::ERROR_PERM_DENIED); 581 | } 582 | if (($file = $this->file($hash)) == false) { 583 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); 584 | } 585 | if (!$file['write'] || !$file['read']) { 586 | return $this->setError(elFinder::ERROR_PERM_DENIED); 587 | } 588 | $path = $this->decode($hash); 589 | if (!$this->canResize($path, $file)) { 590 | return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE); 591 | } 592 | if (!$image = $this->imageManager->make($this->_getContents($path))) { 593 | return false; 594 | } 595 | switch($mode) { 596 | case 'propresize': 597 | $image->resize($width, $height, function($constraint){ 598 | $constraint->aspectRatio(); 599 | }); 600 | break; 601 | case 'crop': 602 | $image->crop($width, $height, $x, $y); 603 | break; 604 | case 'fitsquare': 605 | $image->fit($width, $height, null, 'center'); 606 | break; 607 | case 'rotate': 608 | $image->rotate($degree); 609 | break; 610 | default: 611 | $image->resize($width, $height); 612 | break; 613 | } 614 | if ($jpgQuality && $image->mime() === 'image/jpeg') { 615 | $result = (string) $image->encode('jpg', $jpgQuality); 616 | } else { 617 | $result = (string) $image->encode(); 618 | } 619 | if ($result && $this->_filePutContents($path, $result)) { 620 | $stat = $this->stat($path); 621 | $stat['width'] = $image->width(); 622 | $stat['height'] = $image->height(); 623 | return $stat; 624 | } 625 | return false; 626 | } 627 | 628 | public function getImageSize($path, $mime = '') 629 | { 630 | $size = false; 631 | if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') { 632 | if ($data = $this->_getContents($path)) { 633 | if ($size = @getimagesizefromstring($data)) { 634 | $size['dimensions'] = $size[0].'x'.$size[1]; 635 | } 636 | } 637 | } 638 | return $size; 639 | } 640 | /** 641 | * Return content URL 642 | * 643 | * @param string $hash file hash 644 | * @param array $options options 645 | * @return string 646 | **/ 647 | public function getContentUrl($hash, $options = array()) 648 | { 649 | if (($file = $this->file($hash)) == false || !$file['url'] || $file['url'] == 1) { 650 | $path = $this->decode($hash); 651 | return $this->fs->getUrl($path); 652 | } 653 | return $file['url']; 654 | } 655 | } 656 | -------------------------------------------------------------------------------- /src/Driver/ElFinderVolumeLocalFileSystem.php: -------------------------------------------------------------------------------- 1 | options['alias'] = ''; // alias to replace root dir name 46 | $this->options['dirMode'] = 0755; // new dirs mode 47 | $this->options['fileMode'] = 0644; // new files mode 48 | $this->options['quarantine'] = '.quarantine'; // quarantine folder name - required to check archive (must be hidden) 49 | $this->options['rootCssClass'] = 'elfinder-navbar-root-local'; 50 | } 51 | 52 | /*********************************************************************/ 53 | /* INIT AND CONFIGURE */ 54 | /*********************************************************************/ 55 | 56 | /** 57 | * Prepare driver before mount volume. 58 | * Return true if volume is ready. 59 | * 60 | * @return bool 61 | **/ 62 | protected function init() { 63 | // Normalize directory separator for windows 64 | if (DIRECTORY_SEPARATOR !== '/') { 65 | foreach(array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) { 66 | if (!empty($this->options[$key])) { 67 | $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]); 68 | } 69 | } 70 | } 71 | if (!$cwd = getcwd()) { 72 | return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().'); 73 | } 74 | // detect systemRoot 75 | if (!isset($this->options['systemRoot'])) { 76 | if ($cwd[0] === $this->separator || $this->root[0] === $this->separator) { 77 | $this->systemRoot = $this->separator; 78 | } else if (preg_match('/^([a-zA-Z]:'.preg_quote($this->separator, '/').')/', $this->root, $m)) { 79 | $this->systemRoot = $m[1]; 80 | } else if (preg_match('/^([a-zA-Z]:'.preg_quote($this->separator, '/').')/', $cwd, $m)) { 81 | $this->systemRoot = $m[1]; 82 | } 83 | } 84 | $this->root = $this->getFullPath($this->root, $cwd); 85 | if (!empty($this->options['startPath'])) { 86 | $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $cwd); 87 | } 88 | 89 | if (is_null($this->options['syncChkAsTs'])) { 90 | $this->options['syncChkAsTs'] = true; 91 | } 92 | if (is_null($this->options['syncCheckFunc'])) { 93 | $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify'); 94 | } 95 | 96 | return true; 97 | } 98 | 99 | /** 100 | * Configure after successfull mount. 101 | * 102 | * @return void 103 | * @author Dmitry (dio) Levashov 104 | **/ 105 | protected function configure() { 106 | $root = $this->stat($this->root); 107 | 108 | // chek thumbnails path 109 | if ($this->options['tmbPath']) { 110 | $this->options['tmbPath'] = strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false 111 | // tmb path set as dirname under root dir 112 | ? $this->_abspath($this->options['tmbPath']) 113 | // tmb path as full path 114 | : $this->_normpath($this->options['tmbPath']); 115 | } 116 | 117 | parent::configure(); 118 | 119 | // set $this->tmp by options['tmpPath'] 120 | $this->tmp = ''; 121 | if (!empty($this->options['tmpPath'])) { 122 | if ((is_dir($this->options['tmpPath']) || @mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) { 123 | $this->tmp = $this->options['tmpPath']; 124 | } 125 | } 126 | if (!$this->tmp && ($tmp = ElFinder::getStaticVar('commonTempPath'))) { 127 | $this->tmp = $tmp; 128 | } 129 | 130 | // if no thumbnails url - try detect it 131 | if ($root['read'] && !$this->tmbURL && $this->URL) { 132 | if (strpos($this->tmbPath, $this->root) === 0) { 133 | $this->tmbURL = $this->URL.str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root)+1)); 134 | if (preg_match("|[^/?&=]$|", $this->tmbURL)) { 135 | $this->tmbURL .= '/'; 136 | } 137 | } 138 | } 139 | 140 | // check quarantine dir 141 | $this->quarantine = ''; 142 | if (!empty($this->options['quarantine'])) { 143 | if (is_dir($this->options['quarantine'])) { 144 | if (is_writable($this->options['quarantine'])) { 145 | $this->quarantine = $this->options['quarantine']; 146 | } 147 | $this->options['quarantine'] = ''; 148 | } else { 149 | $this->quarantine = $this->_abspath($this->options['quarantine']); 150 | if ((!is_dir($this->quarantine) && !$this->_mkdir($this->root, $this->options['quarantine'])) || !is_writable($this->quarantine)) { 151 | $this->options['quarantine'] = $this->quarantine = ''; 152 | } 153 | } 154 | } 155 | 156 | if (!$this->quarantine) { 157 | $this->archivers['extract'] = array(); 158 | $this->disabled[] = 'extract'; 159 | } 160 | 161 | if ($this->options['quarantine']) { 162 | $this->attributes[] = array( 163 | 'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR.$this->options['quarantine']).'$~', 164 | 'read' => false, 165 | 'write' => false, 166 | 'locked' => true, 167 | 'hidden' => true 168 | ); 169 | } 170 | } 171 | 172 | /** 173 | * Long pooling sync checker 174 | * This function require server command `inotifywait` 175 | * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php 176 | * 177 | * @param string $path 178 | * @param int $standby 179 | * @param number $compare 180 | * @return number|bool 181 | */ 182 | public function localFileSystemInotify($path, $standby, $compare) { 183 | if (isset($this->sessionCache['localFileSystemInotify_disable'])) { 184 | return false; 185 | } 186 | $path = realpath($path); 187 | $mtime = filemtime($path); 188 | if ($mtime != $compare) { 189 | return $mtime; 190 | } 191 | $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH')? ELFINER_INOTIFYWAIT_PATH : 'inotifywait'; 192 | $path = escapeshellarg($path); 193 | $standby = max(1, intval($standby)); 194 | $cmd = $inotifywait.' '.$path.' -t '.$standby.' -e moved_to,moved_from,move,close_write,delete,delete_self'; 195 | $this->procExec($cmd , $o, $r); 196 | if ($r === 0) { 197 | // changed 198 | clearstatcache(); 199 | $mtime = @filemtime($path); // error on busy? 200 | return $mtime? $mtime : time(); 201 | } else if ($r === 2) { 202 | // not changed (timeout) 203 | return $compare; 204 | } 205 | // error 206 | // cache to $_SESSION 207 | $sessionStart = $this->sessionRestart(); 208 | if ($sessionStart) { 209 | $this->sessionCache['localFileSystemInotify_disable'] = true; 210 | ElFinder::sessionWrite(); 211 | } 212 | 213 | return false; 214 | } 215 | 216 | /*********************************************************************/ 217 | /* FS API */ 218 | /*********************************************************************/ 219 | 220 | /*********************** paths/urls *************************/ 221 | 222 | /** 223 | * Return parent directory path 224 | * 225 | * @param string $path file path 226 | * @return string 227 | * @author Dmitry (dio) Levashov 228 | **/ 229 | protected function _dirname($path) { 230 | return dirname($path); 231 | } 232 | 233 | /** 234 | * Return file name 235 | * 236 | * @param string $path file path 237 | * @return string 238 | * @author Dmitry (dio) Levashov 239 | **/ 240 | protected function _basename($path) { 241 | return basename($path); 242 | } 243 | 244 | /** 245 | * Join dir name and file name and retur full path 246 | * 247 | * @param string $dir 248 | * @param string $name 249 | * @return string 250 | * @author Dmitry (dio) Levashov 251 | **/ 252 | protected function _joinPath($dir, $name) { 253 | return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name; 254 | } 255 | 256 | /** 257 | * Return normalized path, this works the same as os.path.normpath() in Python 258 | * 259 | * @param string $path path 260 | * @return string 261 | * @author Troex Nevelin 262 | **/ 263 | protected function _normpath($path) { 264 | if (empty($path)) { 265 | return '.'; 266 | } 267 | 268 | $changeSep = (DIRECTORY_SEPARATOR !== '/'); 269 | if ($changeSep) { 270 | $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); 271 | } 272 | 273 | if (strpos($path, '/') === 0) { 274 | $initial_slashes = true; 275 | } else { 276 | $initial_slashes = false; 277 | } 278 | 279 | if (($initial_slashes) 280 | && (strpos($path, '//') === 0) 281 | && (strpos($path, '///') === false)) { 282 | $initial_slashes = 2; 283 | } 284 | 285 | $initial_slashes = (int) $initial_slashes; 286 | 287 | $comps = explode('/', $path); 288 | $new_comps = array(); 289 | foreach ($comps as $comp) { 290 | if (in_array($comp, array('', '.'))) { 291 | continue; 292 | } 293 | 294 | if (($comp != '..') 295 | || (!$initial_slashes && !$new_comps) 296 | || ($new_comps && (end($new_comps) == '..'))) { 297 | array_push($new_comps, $comp); 298 | } elseif ($new_comps) { 299 | array_pop($new_comps); 300 | } 301 | } 302 | $comps = $new_comps; 303 | $path = implode('/', $comps); 304 | if ($initial_slashes) { 305 | $path = str_repeat('/', $initial_slashes) . $path; 306 | } 307 | 308 | if ($changeSep) { 309 | $path = str_replace('/', DIRECTORY_SEPARATOR, $path); 310 | } 311 | 312 | return $path ? $path : '.'; 313 | } 314 | 315 | /** 316 | * Return file path related to root dir 317 | * 318 | * @param string $path file path 319 | * @return string 320 | * @author Dmitry (dio) Levashov 321 | **/ 322 | protected function _relpath($path) { 323 | if ($path === $this->root) { 324 | return ''; 325 | } else { 326 | if (strpos($path, $this->root) === 0) { 327 | return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR); 328 | } else { 329 | // for link 330 | return $path; 331 | } 332 | } 333 | } 334 | 335 | /** 336 | * Convert path related to root dir into real path 337 | * 338 | * @param string $path file path 339 | * @return string 340 | * @author Dmitry (dio) Levashov 341 | **/ 342 | protected function _abspath($path) { 343 | if ($path === DIRECTORY_SEPARATOR) { 344 | return $this->root; 345 | } else { 346 | if ($path[0] === DIRECTORY_SEPARATOR) { 347 | // for link 348 | return $path; 349 | } else { 350 | return $this->_joinPath($this->root, $path); 351 | } 352 | } 353 | } 354 | 355 | /** 356 | * Return fake path started from root dir 357 | * 358 | * @param string $path file path 359 | * @return string 360 | * @author Dmitry (dio) Levashov 361 | **/ 362 | protected function _path($path) { 363 | return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path)); 364 | } 365 | 366 | /** 367 | * Return true if $path is children of $parent 368 | * 369 | * @param string $path path to check 370 | * @param string $parent parent path 371 | * @return bool 372 | * @author Dmitry (dio) Levashov 373 | **/ 374 | protected function _inpath($path, $parent) { 375 | $cwd = getcwd(); 376 | $real_path = $this->getFullPath($path, $cwd); 377 | $real_parent = $this->getFullPath($parent, $cwd); 378 | if ($real_path && $real_parent) { 379 | return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR) === 0; 380 | } 381 | return false; 382 | } 383 | 384 | 385 | 386 | /***************** file stat ********************/ 387 | 388 | /** 389 | * Return stat for given path. 390 | * Stat contains following fields: 391 | * - (int) size file size in b. required 392 | * - (int) ts file modification time in unix time. required 393 | * - (string) mime mimetype. required for folders, others - optionally 394 | * - (bool) read read permissions. required 395 | * - (bool) write write permissions. required 396 | * - (bool) locked is object locked. optionally 397 | * - (bool) hidden is object hidden. optionally 398 | * - (string) alias for symlinks - link target path relative to root path. optionally 399 | * - (string) target for symlinks - link target path. optionally 400 | * 401 | * If file does not exists - returns empty array or false. 402 | * 403 | * @param string $path file path 404 | * @return array|false 405 | * @author Dmitry (dio) Levashov 406 | **/ 407 | protected function _stat($path) { 408 | 409 | static $statOwner; 410 | if (is_null($statOwner)) { 411 | $statOwner = (!empty($this->options['statOwner'])); 412 | } 413 | 414 | $stat = array(); 415 | 416 | if (!file_exists($path) && !is_link($path)) { 417 | return $stat; 418 | } 419 | 420 | //Verifies the given path is the root or is inside the root. Prevents directory traveral. 421 | if (!$this->_inpath($path, $this->root)) { 422 | return $stat; 423 | } 424 | 425 | $gid = $uid = 0; 426 | $stat['isowner'] = false; 427 | $linkreadable = false; 428 | if ($path != $this->root && is_link($path)) { 429 | if (!($target = $this->readlink($path)) 430 | || $target == $path) { 431 | if (is_null($target)) { 432 | $stat = array(); 433 | return $stat; 434 | } else { 435 | $stat['mime'] = 'symlink-broken'; 436 | $target = readlink($path); 437 | $lstat = lstat($path); 438 | $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']); 439 | $linkreadable = !empty($ostat['isowner']); 440 | } 441 | } 442 | $stat['alias'] = $this->_path($target); 443 | $stat['target'] = $target; 444 | } 445 | $size = sprintf('%u', @filesize($path)); 446 | $stat['ts'] = filemtime($path); 447 | if ($statOwner) { 448 | $fstat = stat($path); 449 | $uid = $fstat['uid']; 450 | $gid = $fstat['gid']; 451 | $stat['perm'] = substr((string)decoct($fstat['mode']), -4); 452 | $stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); 453 | } 454 | 455 | $dir = is_dir($path); 456 | 457 | if (!isset($stat['mime'])) { 458 | $stat['mime'] = $dir ? 'directory' : $this->mimetype($path); 459 | } 460 | //logical rights first 461 | $stat['read'] = ($linkreadable || is_readable($path))? null : false; 462 | $stat['write'] = is_writable($path)? null : false; 463 | 464 | if (is_null($stat['read'])) { 465 | $stat['size'] = $dir ? 0 : $size; 466 | } 467 | 468 | return $stat; 469 | } 470 | 471 | /** 472 | * Get stat `owner`, `group` and `isowner` by `uid` and `gid` 473 | * Sub-fuction of _stat() and _scandir() 474 | * 475 | * @param integer $uid 476 | * @param integer $gid 477 | * @return array stat 478 | */ 479 | protected function getOwnerStat($uid, $gid) { 480 | static $names = null; 481 | static $phpuid = null; 482 | 483 | if (is_null($names)) { 484 | $names = array('uid' => array(), 'gid' =>array()); 485 | } 486 | if (is_null($phpuid)) { 487 | if (is_callable('posix_getuid')) { 488 | $phpuid = posix_getuid(); 489 | } else { 490 | $phpuid = 0; 491 | } 492 | } 493 | 494 | $stat = array(); 495 | 496 | if ($uid) { 497 | $stat['isowner'] = ($phpuid == $uid); 498 | if (isset($names['uid'][$uid])) { 499 | $stat['owner'] = $names['uid'][$uid]; 500 | } else if (is_callable('posix_getpwuid')) { 501 | $pwuid = posix_getpwuid($uid); 502 | $stat['owner'] = $names['uid'][$uid] = $pwuid['name']; 503 | } else { 504 | $stat['owner'] = $names['uid'][$uid] = $uid; 505 | } 506 | } 507 | if ($gid) { 508 | if (isset($names['gid'][$gid])) { 509 | $stat['group'] = $names['gid'][$gid]; 510 | } else if (is_callable('posix_getgrgid')) { 511 | $grgid = posix_getgrgid($gid); 512 | $stat['group'] = $names['gid'][$gid] = $grgid['name']; 513 | } else { 514 | $stat['group'] = $names['gid'][$gid] = $gid; 515 | } 516 | } 517 | 518 | return $stat; 519 | } 520 | 521 | /** 522 | * Return true if path is dir and has at least one childs directory 523 | * 524 | * @param string $path dir path 525 | * @return bool 526 | * @author Dmitry (dio) Levashov 527 | **/ 528 | protected function _subdirs($path) { 529 | 530 | $dirs = false; 531 | if (is_dir($path)) { 532 | $dirItr = new \ParentIterator( 533 | new \RecursiveDirectoryIterator($path, 534 | \FilesystemIterator::SKIP_DOTS | 535 | (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS')? 536 | \RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0) 537 | ) 538 | ); 539 | $dirItr->rewind(); 540 | if ($dirItr->hasChildren()) { 541 | $dirs = true; 542 | $name = $dirItr->getSubPathName(); 543 | while($name) { 544 | if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) { 545 | $dirs = false; 546 | $dirItr->next(); 547 | $name = $dirItr->getSubPathName(); 548 | continue; 549 | } 550 | $dirs = true; 551 | break; 552 | } 553 | } 554 | } 555 | return $dirs; 556 | } 557 | 558 | /** 559 | * Return object width and height 560 | * Usualy used for images, but can be realize for video etc... 561 | * 562 | * @param string $path file path 563 | * @param string $mime file mime type 564 | * @return string 565 | * @author Dmitry (dio) Levashov 566 | **/ 567 | protected function _dimensions($path, $mime) { 568 | clearstatcache(); 569 | return strpos($mime, 'image') === 0 && ($s = @getimagesize($path)) !== false 570 | ? $s[0].'x'.$s[1] 571 | : false; 572 | } 573 | /******************** file/dir content *********************/ 574 | 575 | /** 576 | * Return symlink target file 577 | * 578 | * @param string $path link path 579 | * @return string 580 | * @author Dmitry (dio) Levashov 581 | **/ 582 | protected function readlink($path) { 583 | if (!($target = @readlink($path))) { 584 | return null; 585 | } 586 | 587 | if (strpos($target, $this->systemRoot) !== 0) { 588 | $target = $this->_joinPath(dirname($path), $target); 589 | } 590 | 591 | if (!file_exists($target)) { 592 | return false; 593 | } 594 | 595 | return $target; 596 | } 597 | 598 | /** 599 | * Return files list in directory. 600 | * 601 | * @param string $path dir path 602 | * @return array 603 | * @author Dmitry (dio) Levashov 604 | **/ 605 | protected function _scandir($path) { 606 | $files = array(); 607 | $cache = array(); 608 | $statOwner = (!empty($this->options['statOwner'])); 609 | $dirItr = array(); 610 | try { 611 | $dirItr = new \DirectoryIterator($path); 612 | } catch (\UnexpectedValueException $e) {} 613 | 614 | foreach ($dirItr as $file) { 615 | try { 616 | if ($file->isDot()) { continue; } 617 | 618 | $files[] = $fpath = $file->getPathname(); 619 | 620 | $br = false; 621 | $stat = array(); 622 | 623 | $gid = $uid = 0; 624 | $stat['isowner'] = false; 625 | $linkreadable = false; 626 | if ($file->isLink()) { 627 | if (!($target = $this->readlink($fpath)) 628 | || $target == $fpath) { 629 | if (is_null($target)) { 630 | $stat = array(); 631 | $br = true; 632 | } else { 633 | $_path = $fpath; 634 | $stat['mime'] = 'symlink-broken'; 635 | $target = readlink($_path); 636 | $lstat = lstat($_path); 637 | $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']); 638 | $linkreadable = !empty($ostat['isowner']); 639 | $dir = false; 640 | $stat['alias'] = $this->_path($target); 641 | $stat['target'] = $target; 642 | } 643 | } else { 644 | $dir = is_dir($target); 645 | $stat['alias'] = $this->_path($target); 646 | $stat['target'] = $target; 647 | $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']); 648 | } 649 | } else { 650 | $dir = $file->isDir(); 651 | $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath); 652 | } 653 | $size = sprintf('%u', $file->getSize()); 654 | $stat['ts'] = $file->getMTime(); 655 | if (!$br) { 656 | if ($statOwner && !$linkreadable) { 657 | $uid = $file->getOwner(); 658 | $gid = $file->getGroup(); 659 | $stat['perm'] = substr((string)decoct($file->getPerms()), -4); 660 | $stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); 661 | } 662 | 663 | //logical rights first 664 | $stat['read'] = ($linkreadable || $file->isReadable())? null : false; 665 | $stat['write'] = $file->isWritable()? null : false; 666 | 667 | if (is_null($stat['read'])) { 668 | $stat['size'] = $dir ? 0 : $size; 669 | } 670 | 671 | } 672 | 673 | $cache[] = array($fpath, $stat); 674 | } catch (\RuntimeException $e) { 675 | continue; 676 | } 677 | } 678 | 679 | if ($cache) { 680 | $cache = $this->convEncOut($cache, false); 681 | foreach($cache as $d) { 682 | $this->updateCache($d[0], $d[1]); 683 | } 684 | } 685 | 686 | return $files; 687 | } 688 | 689 | /** 690 | * Open file and return file pointer 691 | * 692 | * @param string $path file path 693 | * @param bool $write open file for writing 694 | * @return resource|false 695 | * @author Dmitry (dio) Levashov 696 | **/ 697 | protected function _fopen($path, $mode='rb') { 698 | return @fopen($path, $mode); 699 | } 700 | 701 | /** 702 | * Close opened file 703 | * 704 | * @param resource $fp file pointer 705 | * @return bool 706 | * @author Dmitry (dio) Levashov 707 | **/ 708 | protected function _fclose($fp, $path='') { 709 | return @fclose($fp); 710 | } 711 | 712 | /******************** file/dir manipulations *************************/ 713 | 714 | /** 715 | * Create dir and return created dir path or false on failed 716 | * 717 | * @param string $path parent dir path 718 | * @param string $name new directory name 719 | * @return string|bool 720 | * @author Dmitry (dio) Levashov 721 | **/ 722 | protected function _mkdir($path, $name) { 723 | $path = $this->_joinPath($path, $name); 724 | 725 | if (@mkdir($path)) { 726 | @chmod($path, $this->options['dirMode']); 727 | clearstatcache(); 728 | return $path; 729 | } 730 | 731 | return false; 732 | } 733 | 734 | /** 735 | * Create file and return it's path or false on failed 736 | * 737 | * @param string $path parent dir path 738 | * @param string $name new file name 739 | * @return string|bool 740 | * @author Dmitry (dio) Levashov 741 | **/ 742 | protected function _mkfile($path, $name) { 743 | $path = $this->_joinPath($path, $name); 744 | 745 | if (($fp = @fopen($path, 'w'))) { 746 | @fclose($fp); 747 | @chmod($path, $this->options['fileMode']); 748 | clearstatcache(); 749 | return $path; 750 | } 751 | return false; 752 | } 753 | 754 | /** 755 | * Create symlink 756 | * 757 | * @param string $source file to link to 758 | * @param string $targetDir folder to create link in 759 | * @param string $name symlink name 760 | * @return bool 761 | * @author Dmitry (dio) Levashov 762 | **/ 763 | protected function _symlink($source, $targetDir, $name) { 764 | return @symlink($source, $this->_joinPath($targetDir, $name)); 765 | } 766 | 767 | /** 768 | * Copy file into another file 769 | * 770 | * @param string $source source file path 771 | * @param string $targetDir target directory path 772 | * @param string $name new file name 773 | * @return bool 774 | * @author Dmitry (dio) Levashov 775 | **/ 776 | protected function _copy($source, $targetDir, $name) { 777 | $ret = copy($source, $this->_joinPath($targetDir, $name)); 778 | $ret && clearstatcache(); 779 | return $ret; 780 | } 781 | 782 | /** 783 | * Move file into another parent dir. 784 | * Return new file path or false. 785 | * 786 | * @param string $source source file path 787 | * @param string $target target dir path 788 | * @param string $name file name 789 | * @return string|bool 790 | * @author Dmitry (dio) Levashov 791 | **/ 792 | protected function _move($source, $targetDir, $name) { 793 | $target = $this->_joinPath($targetDir, $name); 794 | $ret = @rename($source, $target) ? $target : false; 795 | $ret && clearstatcache(); 796 | return $ret; 797 | } 798 | 799 | /** 800 | * Remove file 801 | * 802 | * @param string $path file path 803 | * @return bool 804 | * @author Dmitry (dio) Levashov 805 | **/ 806 | protected function _unlink($path) { 807 | $ret = @unlink($path); 808 | $ret && clearstatcache(); 809 | return $ret; 810 | } 811 | 812 | /** 813 | * Remove dir 814 | * 815 | * @param string $path dir path 816 | * @return bool 817 | * @author Dmitry (dio) Levashov 818 | **/ 819 | protected function _rmdir($path) { 820 | $ret = @rmdir($path); 821 | $ret && clearstatcache(); 822 | return $ret; 823 | } 824 | 825 | /** 826 | * Create new file and write into it from file pointer. 827 | * Return new file path or false on error. 828 | * 829 | * @param resource $fp file pointer 830 | * @param string $dir target dir path 831 | * @param string $name file name 832 | * @param array $stat file stat (required by some virtual fs) 833 | * @return bool|string 834 | * @author Dmitry (dio) Levashov 835 | **/ 836 | protected function _save($fp, $dir, $name, $stat) { 837 | $path = $this->_joinPath($dir, $name); 838 | 839 | $meta = stream_get_meta_data($fp); 840 | $uri = isset($meta['uri'])? $meta['uri'] : ''; 841 | if ($uri && @is_file($uri)) { 842 | fclose($fp); 843 | $isCmdPaste = ($this->ARGS['cmd'] === 'paste'); 844 | $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut'])); 845 | if (($isCmdCopy || !@rename($uri, $path)) && !@copy($uri, $path)) { 846 | return false; 847 | } 848 | // re-create the source file for remove processing of paste command 849 | $isCmdPaste && !$isCmdCopy && touch($uri); 850 | } else { 851 | if (@file_put_contents($path, $fp, LOCK_EX) === false) { 852 | return false; 853 | } 854 | } 855 | 856 | @chmod($path, $this->options['fileMode']); 857 | clearstatcache(); 858 | return $path; 859 | } 860 | 861 | /** 862 | * Get file contents 863 | * 864 | * @param string $path file path 865 | * @return string|false 866 | * @author Dmitry (dio) Levashov 867 | **/ 868 | protected function _getContents($path) { 869 | return file_get_contents($path); 870 | } 871 | 872 | /** 873 | * Write a string to a file 874 | * 875 | * @param string $path file path 876 | * @param string $content new file content 877 | * @return bool 878 | * @author Dmitry (dio) Levashov 879 | **/ 880 | protected function _filePutContents($path, $content) { 881 | if (@file_put_contents($path, $content, LOCK_EX) !== false) { 882 | clearstatcache(); 883 | return true; 884 | } 885 | return false; 886 | } 887 | 888 | /** 889 | * Detect available archivers 890 | * 891 | * @return void 892 | **/ 893 | protected function _checkArchivers() { 894 | $this->archivers = $this->getArchivers(); 895 | return; 896 | } 897 | 898 | /** 899 | * chmod availability 900 | * 901 | * @return bool 902 | **/ 903 | protected function _chmod($path, $mode) { 904 | $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o",$mode)); 905 | $ret = @chmod($path, $modeOct); 906 | $ret && clearstatcache(); 907 | return $ret; 908 | } 909 | 910 | /** 911 | * Recursive symlinks search 912 | * 913 | * @param string $path file/dir path 914 | * @return bool 915 | * @author Dmitry (dio) Levashov 916 | **/ 917 | protected function _findSymlinks($path) { 918 | if (is_link($path)) { 919 | return true; 920 | } 921 | 922 | if (is_dir($path)) { 923 | foreach (scandir($path) as $name) { 924 | if ($name != '.' && $name != '..') { 925 | $p = $path.DIRECTORY_SEPARATOR.$name; 926 | if (is_link($p) || !$this->nameAccepted($name) 927 | || 928 | (($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) { 929 | $this->setError(elFinder::ERROR_SAVE, $name); 930 | return true; 931 | } 932 | if (is_dir($p) && $this->_findSymlinks($p)) { 933 | return true; 934 | } elseif (is_file($p)) { 935 | $this->archiveSize += sprintf('%u', filesize($p)); 936 | } 937 | } 938 | } 939 | } else { 940 | 941 | $this->archiveSize += sprintf('%u', filesize($path)); 942 | } 943 | 944 | return false; 945 | } 946 | 947 | /** 948 | * Extract files from archive 949 | * 950 | * @param string $path archive path 951 | * @param array $arc archiver command and arguments (same as in $this->archivers) 952 | * @return true 953 | * @author Dmitry (dio) Levashov, 954 | * @author Alexey Sukhotin 955 | **/ 956 | protected function _extract($path, $arc) { 957 | 958 | if ($this->quarantine) { 959 | 960 | $dir = $this->quarantine.DIRECTORY_SEPARATOR.md5(basename($path).mt_rand()); 961 | $archive = $dir.DIRECTORY_SEPARATOR.basename($path); 962 | 963 | if (!@mkdir($dir)) { 964 | return false; 965 | } 966 | 967 | // insurance unexpected shutdown 968 | register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir)); 969 | 970 | chmod($dir, 0777); 971 | 972 | // copy in quarantine 973 | if (!copy($path, $archive)) { 974 | return false; 975 | } 976 | 977 | // extract in quarantine 978 | $this->unpackArchive($archive, $arc); 979 | 980 | // get files list 981 | $ls = array(); 982 | foreach (scandir($dir) as $i => $name) { 983 | if ($name != '.' && $name != '..') { 984 | $ls[] = $name; 985 | } 986 | } 987 | 988 | // no files - extract error ? 989 | if (empty($ls)) { 990 | return false; 991 | } 992 | 993 | $this->archiveSize = 0; 994 | 995 | // find symlinks 996 | $symlinks = $this->_findSymlinks($dir); 997 | 998 | if ($symlinks) { 999 | $this->delTree($dir); 1000 | return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS))); 1001 | } 1002 | 1003 | // check max files size 1004 | if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { 1005 | $this->delTree($dir); 1006 | return $this->setError(elFinder::ERROR_ARC_MAXSIZE); 1007 | } 1008 | 1009 | $extractTo = $this->extractToNewdir; // 'auto', ture or false 1010 | 1011 | // archive contains one item - extract in archive dir 1012 | $name = ''; 1013 | $src = $dir.DIRECTORY_SEPARATOR.$ls[0]; 1014 | if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) { 1015 | $name = $ls[0]; 1016 | } else if ($extractTo === 'auto' || $extractTo) { 1017 | // for several files - create new directory 1018 | // create unique name for directory 1019 | $src = $dir; 1020 | $name = basename($path); 1021 | if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) { 1022 | $name = substr($name, 0, strlen($name)-strlen($m[0])); 1023 | } 1024 | $test = dirname($path).DIRECTORY_SEPARATOR.$name; 1025 | if (file_exists($test) || is_link($test)) { 1026 | $name = $this->uniqueName(dirname($path), $name, '-', false); 1027 | } 1028 | } 1029 | 1030 | if ($name !== '') { 1031 | $result = dirname($path).DIRECTORY_SEPARATOR.$name; 1032 | 1033 | if (! @rename($src, $result)) { 1034 | $this->delTree($dir); 1035 | return false; 1036 | } 1037 | } else { 1038 | $dstDir = dirname($path); 1039 | $res = false; 1040 | $result = array(); 1041 | foreach($ls as $name) { 1042 | $target = $dstDir.DIRECTORY_SEPARATOR.$name; 1043 | if (is_dir($target)) { 1044 | $this->delTree($target); 1045 | } 1046 | if (@rename($dir.DIRECTORY_SEPARATOR.$name, $target)) { 1047 | $result[] = $target; 1048 | } 1049 | } 1050 | if (!$result) { 1051 | $this->delTree($dir); 1052 | return false; 1053 | } 1054 | } 1055 | 1056 | is_dir($dir) && $this->delTree($dir); 1057 | 1058 | return (is_array($result) || file_exists($result)) ? $result : false; 1059 | } 1060 | } 1061 | 1062 | /** 1063 | * Create archive and return its path 1064 | * 1065 | * @param string $dir target dir 1066 | * @param array $files files names list 1067 | * @param string $name archive name 1068 | * @param array $arc archiver options 1069 | * @return string|bool 1070 | * @author Dmitry (dio) Levashov, 1071 | * @author Alexey Sukhotin 1072 | **/ 1073 | protected function _archive($dir, $files, $name, $arc) { 1074 | return $this->makeArchive($dir, $files, $name, $arc); 1075 | } 1076 | 1077 | /******************** Over write functions *************************/ 1078 | 1079 | /** 1080 | * File path of local server side work file path 1081 | * 1082 | * @param string $path 1083 | * @return string 1084 | * @author Naoki Sawada 1085 | */ 1086 | protected function getWorkFile($path) { 1087 | return $path; 1088 | } 1089 | 1090 | /** 1091 | * Delete dirctory trees 1092 | * 1093 | * @param string $localpath path need convert encoding to server encoding 1094 | * @return boolean 1095 | * @author Naoki Sawada 1096 | */ 1097 | protected function delTree($localpath) { 1098 | return $this->rmdirRecursive($localpath); 1099 | } 1100 | 1101 | /******************** Over write (Optimized) functions *************************/ 1102 | 1103 | /** 1104 | * Recursive files search 1105 | * 1106 | * @param string $path dir path 1107 | * @param string $q search string 1108 | * @param array $mimes 1109 | * @return array 1110 | * @author Dmitry (dio) Levashov 1111 | * @author Naoki Sawada 1112 | **/ 1113 | protected function doSearch($path, $q, $mimes) { 1114 | if ($this->encoding) { 1115 | // non UTF-8 use elFinderVolumeDriver::doSearch() 1116 | return parent::doSearch($path, $q, $mimes); 1117 | } 1118 | 1119 | $this->doSearchCurrentQuery = $q; 1120 | $match = array(); 1121 | try { 1122 | $iterator = new \RecursiveIteratorIterator( 1123 | new \RecursiveCallbackFilterIterator( 1124 | new \RecursiveDirectoryIterator($path, 1125 | \FilesystemIterator::KEY_AS_PATHNAME | 1126 | \FilesystemIterator::SKIP_DOTS | 1127 | (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS')? 1128 | \RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0) 1129 | ), 1130 | array($this, 'localFileSystemSearchIteratorFilter') 1131 | ), 1132 | \RecursiveIteratorIterator::SELF_FIRST, 1133 | \RecursiveIteratorIterator::CATCH_GET_CHILD 1134 | ); 1135 | foreach ($iterator as $key => $node) { 1136 | if ($node->isDir()) { 1137 | if ($this->stripos($node->getFilename(), $q) !== false) { 1138 | $match[] = $key; 1139 | } 1140 | } else { 1141 | $match[] = $key; 1142 | } 1143 | } 1144 | } catch (\Exception $e) {} 1145 | 1146 | $result = array(); 1147 | 1148 | if ($match) { 1149 | foreach($match as $p) { 1150 | $stat = $this->stat($p); 1151 | 1152 | if (!$stat) { // invalid links 1153 | continue; 1154 | } 1155 | 1156 | if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) { 1157 | continue; 1158 | } 1159 | 1160 | $name = $stat['name']; 1161 | 1162 | if ((!$mimes || $stat['mime'] !== 'directory')) { 1163 | $stat['path'] = $this->path($stat['hash']); 1164 | if ($this->URL && !isset($stat['url'])) { 1165 | $path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1)); 1166 | $stat['url'] = $this->URL . $path; 1167 | } 1168 | 1169 | $result[] = $stat; 1170 | } 1171 | } 1172 | } 1173 | 1174 | return $result; 1175 | } 1176 | 1177 | /******************** Original local functions *************************/ 1178 | 1179 | public function localFileSystemSearchIteratorFilter($file, $key, $iterator) { 1180 | if ($iterator->hasChildren()) { 1181 | return (bool)$this->attr($key, 'read', null, true); 1182 | } 1183 | return ($this->stripos($file->getFilename(), $this->doSearchCurrentQuery) === false)? false : true; 1184 | } 1185 | 1186 | } // END class 1187 | -------------------------------------------------------------------------------- /src/Driver/ElFinderVolumeMySQL.php: -------------------------------------------------------------------------------- 1 | 'localhost', 68 | 'user' => '', 69 | 'pass' => '', 70 | 'db' => '', 71 | 'port' => null, 72 | 'socket' => null, 73 | 'files_table' => 'elfinder_file', 74 | 'tmbPath' => '', 75 | 'tmpPath' => '' 76 | ); 77 | $this->options = array_merge($this->options, $opts); 78 | $this->options['mimeDetect'] = 'internal'; 79 | } 80 | 81 | /*********************************************************************/ 82 | /* INIT AND CONFIGURE */ 83 | /*********************************************************************/ 84 | 85 | /** 86 | * Prepare driver before mount volume. 87 | * Connect to db, check required tables and fetch root path 88 | * 89 | * @return bool 90 | * @author Dmitry (dio) Levashov 91 | **/ 92 | protected function init() { 93 | 94 | if (!($this->options['host'] || $this->options['socket']) 95 | || !$this->options['user'] 96 | || !$this->options['pass'] 97 | || !$this->options['db'] 98 | || !$this->options['path'] 99 | || !$this->options['files_table']) { 100 | return false; 101 | } 102 | 103 | 104 | $this->db = new \mysqli($this->options['host'], $this->options['user'], $this->options['pass'], $this->options['db'], $this->options['port'], $this->options['socket']); 105 | if ($this->db->connect_error || @mysqli_connect_error()) { 106 | return false; 107 | } 108 | 109 | $this->db->set_charset('utf8'); 110 | 111 | if ($res = $this->db->query('SHOW TABLES')) { 112 | while ($row = $res->fetch_array()) { 113 | if ($row[0] == $this->options['files_table']) { 114 | $this->tbf = $this->options['files_table']; 115 | break; 116 | } 117 | } 118 | } 119 | 120 | if (!$this->tbf) { 121 | return false; 122 | } 123 | 124 | $this->updateCache($this->options['path'], $this->_stat($this->options['path'])); 125 | 126 | return true; 127 | } 128 | 129 | 130 | 131 | /** 132 | * Set tmp path 133 | * 134 | * @return void 135 | * @author Dmitry (dio) Levashov 136 | **/ 137 | protected function configure() { 138 | parent::configure(); 139 | 140 | if (($tmp = $this->options['tmpPath'])) { 141 | if (!file_exists($tmp)) { 142 | if (@mkdir($tmp)) { 143 | @chmod($tmp, $this->options['tmbPathMode']); 144 | } 145 | } 146 | 147 | $this->tmpPath = is_dir($tmp) && is_writable($tmp) ? $tmp : false; 148 | } 149 | 150 | if (!$this->tmpPath && $this->tmbPath && $this->tmbPathWritable) { 151 | $this->tmpPath = $this->tmbPath; 152 | } 153 | 154 | $this->mimeDetect = 'internal'; 155 | } 156 | 157 | /** 158 | * Close connection 159 | * 160 | * @return void 161 | * @author Dmitry (dio) Levashov 162 | **/ 163 | public function umount() { 164 | $this->db->close(); 165 | } 166 | 167 | /** 168 | * Return debug info for client 169 | * 170 | * @return array 171 | * @author Dmitry (dio) Levashov 172 | **/ 173 | public function debug() { 174 | $debug = parent::debug(); 175 | $debug['sqlCount'] = $this->sqlCnt; 176 | if ($this->dbError) { 177 | $debug['dbError'] = $this->dbError; 178 | } 179 | return $debug; 180 | } 181 | 182 | /** 183 | * Perform sql query and return result. 184 | * Increase sqlCnt and save error if occured 185 | * 186 | * @param string $sql query 187 | * @return misc 188 | * @author Dmitry (dio) Levashov 189 | **/ 190 | protected function query($sql) { 191 | $this->sqlCnt++; 192 | $res = $this->db->query($sql); 193 | if (!$res) { 194 | $this->dbError = $this->db->error; 195 | } 196 | return $res; 197 | } 198 | 199 | /** 200 | * Create empty object with required mimetype 201 | * 202 | * @param string $path parent dir path 203 | * @param string $name object name 204 | * @param string $mime mime type 205 | * @return bool 206 | * @author Dmitry (dio) Levashov 207 | **/ 208 | protected function make($path, $name, $mime) { 209 | $sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`) VALUES ("%s", "%s", 0, %d, "%s", "", "%d", "%d")'; 210 | $sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write']); 211 | // echo $sql; 212 | return $this->query($sql) && $this->db->affected_rows > 0; 213 | } 214 | 215 | /** 216 | * Search files 217 | * 218 | * @param string $q search string 219 | * @param array $mimes 220 | * @return array 221 | * @author Dmitry (dio) Levashov 222 | **/ 223 | public function search($q, $mimes) { 224 | $result = array(); 225 | 226 | $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 227 | FROM %s AS f 228 | WHERE f.name RLIKE "%s"'; 229 | 230 | $sql = sprintf($sql, $this->tbf, $this->db->real_escape_string($q)); 231 | 232 | if (($res = $this->query($sql))) { 233 | while ($row = $res->fetch_assoc()) { 234 | if ($this->mimeAccepted($row['mime'], $mimes)) { 235 | $id = $row['id']; 236 | if ($row['parent_id']) { 237 | $row['phash'] = $this->encode($row['parent_id']); 238 | } 239 | 240 | if ($row['mime'] == 'directory') { 241 | unset($row['width']); 242 | unset($row['height']); 243 | } else { 244 | unset($row['dirs']); 245 | } 246 | 247 | unset($row['id']); 248 | unset($row['parent_id']); 249 | 250 | 251 | 252 | if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) { 253 | $result[] = $stat; 254 | } 255 | } 256 | } 257 | } 258 | 259 | return $result; 260 | } 261 | 262 | /** 263 | * Return temporary file path for required file 264 | * 265 | * @param string $path file path 266 | * @return string 267 | * @author Dmitry (dio) Levashov 268 | **/ 269 | protected function tmpname($path) { 270 | return $this->tmpPath.DIRECTORY_SEPARATOR.md5($path); 271 | } 272 | 273 | /** 274 | * Resize image 275 | * 276 | * @param string $hash image file 277 | * @param int $width new width 278 | * @param int $height new height 279 | * @param bool $crop crop image 280 | * @return array|false 281 | * @author Dmitry (dio) Levashov 282 | * @author Alexey Sukhotin 283 | **/ 284 | public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) { 285 | if ($this->commandDisabled('resize')) { 286 | return $this->setError(ElFinder::ERROR_PERM_DENIED); 287 | } 288 | 289 | if (($file = $this->file($hash)) == false) { 290 | return $this->setError(ElFinder::ERROR_FILE_NOT_FOUND); 291 | } 292 | 293 | if (!$file['write'] || !$file['read']) { 294 | return $this->setError(ElFinder::ERROR_PERM_DENIED); 295 | } 296 | 297 | $path = $this->decode($hash); 298 | 299 | if (!$this->canResize($path, $file)) { 300 | return $this->setError(ElFinder::ERROR_UNSUPPORT_TYPE); 301 | } 302 | 303 | $img = $this->tmpname($path); 304 | 305 | if (!($fp = @fopen($img, 'w+'))) { 306 | return false; 307 | } 308 | 309 | if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id="'.$path.'"')) 310 | && ($r = $res->fetch_assoc())) { 311 | fwrite($fp, $r['content']); 312 | rewind($fp); 313 | fclose($fp); 314 | } else { 315 | return false; 316 | } 317 | 318 | 319 | switch($mode) { 320 | 321 | case 'propresize': 322 | $result = $this->imgResize($img, $width, $height, true, true); 323 | break; 324 | 325 | case 'crop': 326 | $result = $this->imgCrop($img, $width, $height, $x, $y); 327 | break; 328 | 329 | case 'fitsquare': 330 | $result = $this->imgSquareFit($img, $width, $height, 'center', 'middle', $bg ? $bg : $this->options['tmbBgColor']); 331 | break; 332 | 333 | default: 334 | $result = $this->imgResize($img, $width, $height, false, true); 335 | break; 336 | } 337 | 338 | if ($result) { 339 | 340 | $sql = sprintf('UPDATE %s SET content=LOAD_FILE("%s"), mtime=UNIX_TIMESTAMP() WHERE id=%d', $this->tbf, $this->loadFilePath($img), $path); 341 | 342 | if (!$this->query($sql)) { 343 | $content = file_get_contents($img); 344 | $sql = sprintf('UPDATE %s SET content="%s", mtime=UNIX_TIMESTAMP() WHERE id=%d', $this->tbf, $this->db->real_escape_string($content), $path); 345 | if (!$this->query($sql)) { 346 | @unlink($img); 347 | return false; 348 | } 349 | } 350 | @unlink($img); 351 | $this->rmTmb($file); 352 | $this->clearcache(); 353 | return $this->stat($path); 354 | } 355 | 356 | return false; 357 | } 358 | 359 | 360 | /*********************************************************************/ 361 | /* FS API */ 362 | /*********************************************************************/ 363 | 364 | /** 365 | * Cache dir contents 366 | * 367 | * @param string $path dir path 368 | * @return void 369 | * @author Dmitry Levashov 370 | **/ 371 | protected function cacheDir($path) { 372 | $this->dirsCache[$path] = array(); 373 | 374 | $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 375 | FROM '.$this->tbf.' AS f 376 | LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime="directory" 377 | WHERE f.parent_id="'.$path.'" 378 | GROUP BY f.id'; 379 | 380 | $res = $this->query($sql); 381 | if ($res) { 382 | while ($row = $res->fetch_assoc()) { 383 | // debug($row); 384 | $id = $row['id']; 385 | if ($row['parent_id']) { 386 | $row['phash'] = $this->encode($row['parent_id']); 387 | } 388 | 389 | if ($row['mime'] == 'directory') { 390 | unset($row['width']); 391 | unset($row['height']); 392 | } else { 393 | unset($row['dirs']); 394 | } 395 | 396 | unset($row['id']); 397 | unset($row['parent_id']); 398 | 399 | 400 | 401 | if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) { 402 | $this->dirsCache[$path][] = $id; 403 | } 404 | } 405 | } 406 | 407 | return $this->dirsCache[$path]; 408 | } 409 | 410 | /** 411 | * Return array of parents paths (ids) 412 | * 413 | * @param int $path file path (id) 414 | * @return array 415 | * @author Dmitry (dio) Levashov 416 | **/ 417 | protected function getParents($path) { 418 | $parents = array(); 419 | 420 | while ($path) { 421 | if ($file = $this->stat($path)) { 422 | array_unshift($parents, $path); 423 | $path = isset($file['phash']) ? $this->decode($file['phash']) : false; 424 | } 425 | } 426 | 427 | if (count($parents)) { 428 | array_pop($parents); 429 | } 430 | return $parents; 431 | } 432 | 433 | /** 434 | * Return correct file path for LOAD_FILE method 435 | * 436 | * @param string $path file path (id) 437 | * @return string 438 | * @author Troex Nevelin 439 | **/ 440 | protected function loadFilePath($path) { 441 | $realPath = realpath($path); 442 | if (DIRECTORY_SEPARATOR == '\\') { // windows 443 | $realPath = str_replace('\\', '\\\\', $realPath); 444 | } 445 | return $this->db->real_escape_string($realPath); 446 | } 447 | 448 | /** 449 | * Recursive files search 450 | * 451 | * @param string $path dir path 452 | * @param string $q search string 453 | * @param array $mimes 454 | * @return array 455 | * @author Dmitry (dio) Levashov 456 | **/ 457 | protected function doSearch($path, $q, $mimes) { 458 | return array(); 459 | } 460 | 461 | 462 | /*********************** paths/urls *************************/ 463 | 464 | /** 465 | * Return parent directory path 466 | * 467 | * @param string $path file path 468 | * @return string 469 | * @author Dmitry (dio) Levashov 470 | **/ 471 | protected function _dirname($path) { 472 | return ($stat = $this->stat($path)) ? ($stat['phash'] ? $this->decode($stat['phash']) : $this->root) : false; 473 | } 474 | 475 | /** 476 | * Return file name 477 | * 478 | * @param string $path file path 479 | * @return string 480 | * @author Dmitry (dio) Levashov 481 | **/ 482 | protected function _basename($path) { 483 | return ($stat = $this->stat($path)) ? $stat['name'] : false; 484 | } 485 | 486 | /** 487 | * Join dir name and file name and return full path 488 | * 489 | * @param string $dir 490 | * @param string $name 491 | * @return string 492 | * @author Dmitry (dio) Levashov 493 | **/ 494 | protected function _joinPath($dir, $name) { 495 | $sql = 'SELECT id FROM '.$this->tbf.' WHERE parent_id="'.$dir.'" AND name="'.$this->db->real_escape_string($name).'"'; 496 | 497 | if (($res = $this->query($sql)) && ($r = $res->fetch_assoc())) { 498 | $this->updateCache($r['id'], $this->_stat($r['id'])); 499 | return $r['id']; 500 | } 501 | return -1; 502 | } 503 | 504 | /** 505 | * Return normalized path, this works the same as os.path.normpath() in Python 506 | * 507 | * @param string $path path 508 | * @return string 509 | * @author Troex Nevelin 510 | **/ 511 | protected function _normpath($path) { 512 | return $path; 513 | } 514 | 515 | /** 516 | * Return file path related to root dir 517 | * 518 | * @param string $path file path 519 | * @return string 520 | * @author Dmitry (dio) Levashov 521 | **/ 522 | protected function _relpath($path) { 523 | return $path; 524 | } 525 | 526 | /** 527 | * Convert path related to root dir into real path 528 | * 529 | * @param string $path file path 530 | * @return string 531 | * @author Dmitry (dio) Levashov 532 | **/ 533 | protected function _abspath($path) { 534 | return $path; 535 | } 536 | 537 | /** 538 | * Return fake path started from root dir 539 | * 540 | * @param string $path file path 541 | * @return string 542 | * @author Dmitry (dio) Levashov 543 | **/ 544 | protected function _path($path) { 545 | if (($file = $this->stat($path)) == false) { 546 | return ''; 547 | } 548 | 549 | $parentsIds = $this->getParents($path); 550 | $path = ''; 551 | foreach ($parentsIds as $id) { 552 | $dir = $this->stat($id); 553 | $path .= $dir['name'].$this->separator; 554 | } 555 | return $path.$file['name']; 556 | } 557 | 558 | /** 559 | * Return true if $path is children of $parent 560 | * 561 | * @param string $path path to check 562 | * @param string $parent parent path 563 | * @return bool 564 | * @author Dmitry (dio) Levashov 565 | **/ 566 | protected function _inpath($path, $parent) { 567 | return $path == $parent 568 | ? true 569 | : in_array($parent, $this->getParents($path)); 570 | } 571 | 572 | /***************** file stat ********************/ 573 | /** 574 | * Return stat for given path. 575 | * Stat contains following fields: 576 | * - (int) size file size in b. required 577 | * - (int) ts file modification time in unix time. required 578 | * - (string) mime mimetype. required for folders, others - optionally 579 | * - (bool) read read permissions. required 580 | * - (bool) write write permissions. required 581 | * - (bool) locked is object locked. optionally 582 | * - (bool) hidden is object hidden. optionally 583 | * - (string) alias for symlinks - link target path relative to root path. optionally 584 | * - (string) target for symlinks - link target path. optionally 585 | * 586 | * If file does not exists - returns empty array or false. 587 | * 588 | * @param string $path file path 589 | * @return array|false 590 | * @author Dmitry (dio) Levashov 591 | **/ 592 | protected function _stat($path) { 593 | $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 594 | FROM '.$this->tbf.' AS f 595 | LEFT JOIN '.$this->tbf.' AS p ON p.id=f.parent_id 596 | LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime="directory" 597 | WHERE f.id="'.$path.'" 598 | GROUP BY f.id'; 599 | 600 | $res = $this->query($sql); 601 | 602 | if ($res) { 603 | $stat = $res->fetch_assoc(); 604 | if ($stat['parent_id']) { 605 | $stat['phash'] = $this->encode($stat['parent_id']); 606 | } 607 | if ($stat['mime'] == 'directory') { 608 | unset($stat['width']); 609 | unset($stat['height']); 610 | } else { 611 | unset($stat['dirs']); 612 | } 613 | unset($stat['id']); 614 | unset($stat['parent_id']); 615 | return $stat; 616 | 617 | } 618 | return array(); 619 | } 620 | 621 | /** 622 | * Return true if path is dir and has at least one childs directory 623 | * 624 | * @param string $path dir path 625 | * @return bool 626 | * @author Dmitry (dio) Levashov 627 | **/ 628 | protected function _subdirs($path) { 629 | return ($stat = $this->stat($path)) && isset($stat['dirs']) ? $stat['dirs'] : false; 630 | } 631 | 632 | /** 633 | * Return object width and height 634 | * Usualy used for images, but can be realize for video etc... 635 | * 636 | * @param string $path file path 637 | * @param string $mime file mime type 638 | * @return string 639 | * @author Dmitry (dio) Levashov 640 | **/ 641 | protected function _dimensions($path, $mime) { 642 | return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'].'x'.$stat['height'] : ''; 643 | } 644 | 645 | /******************** file/dir content *********************/ 646 | 647 | /** 648 | * Return files list in directory. 649 | * 650 | * @param string $path dir path 651 | * @return array 652 | * @author Dmitry (dio) Levashov 653 | **/ 654 | protected function _scandir($path) { 655 | return isset($this->dirsCache[$path]) 656 | ? $this->dirsCache[$path] 657 | : $this->cacheDir($path); 658 | } 659 | 660 | /** 661 | * Open file and return file pointer 662 | * 663 | * @param string $path file path 664 | * @param string $mode open file mode (ignored in this driver) 665 | * @return resource|false 666 | * @author Dmitry (dio) Levashov 667 | **/ 668 | protected function _fopen($path, $mode='rb') { 669 | $fp = $this->tmbPath 670 | ? @fopen($this->tmpname($path), 'w+') 671 | : @tmpfile(); 672 | 673 | 674 | if ($fp) { 675 | if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id="'.$path.'"')) 676 | && ($r = $res->fetch_assoc())) { 677 | fwrite($fp, $r['content']); 678 | rewind($fp); 679 | return $fp; 680 | } else { 681 | $this->_fclose($fp, $path); 682 | } 683 | } 684 | 685 | return false; 686 | } 687 | 688 | /** 689 | * Close opened file 690 | * 691 | * @param resource $fp file pointer 692 | * @return bool 693 | * @author Dmitry (dio) Levashov 694 | **/ 695 | protected function _fclose($fp, $path='') { 696 | @fclose($fp); 697 | if ($path) { 698 | @unlink($this->tmpname($path)); 699 | } 700 | } 701 | 702 | /******************** file/dir manipulations *************************/ 703 | 704 | /** 705 | * Create dir and return created dir path or false on failed 706 | * 707 | * @param string $path parent dir path 708 | * @param string $name new directory name 709 | * @return string|bool 710 | * @author Dmitry (dio) Levashov 711 | **/ 712 | protected function _mkdir($path, $name) { 713 | return $this->make($path, $name, 'directory') ? $this->_joinPath($path, $name) : false; 714 | } 715 | 716 | /** 717 | * Create file and return it's path or false on failed 718 | * 719 | * @param string $path parent dir path 720 | * @param string $name new file name 721 | * @return string|bool 722 | * @author Dmitry (dio) Levashov 723 | **/ 724 | protected function _mkfile($path, $name) { 725 | return $this->make($path, $name, 'text/plain') ? $this->_joinPath($path, $name) : false; 726 | } 727 | 728 | /** 729 | * Create symlink. FTP driver does not support symlinks. 730 | * 731 | * @param string $target link target 732 | * @param string $path symlink path 733 | * @return bool 734 | * @author Dmitry (dio) Levashov 735 | **/ 736 | protected function _symlink($target, $path, $name) { 737 | return false; 738 | } 739 | 740 | /** 741 | * Copy file into another file 742 | * 743 | * @param string $source source file path 744 | * @param string $targetDir target directory path 745 | * @param string $name new file name 746 | * @return bool 747 | * @author Dmitry (dio) Levashov 748 | **/ 749 | protected function _copy($source, $targetDir, $name) { 750 | $this->clearcache(); 751 | $id = $this->_joinPath($targetDir, $name); 752 | 753 | $sql = $id > 0 754 | ? 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) 755 | : 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); 756 | 757 | return $this->query($sql); 758 | } 759 | 760 | /** 761 | * Move file into another parent dir. 762 | * Return new file path or false. 763 | * 764 | * @param string $source source file path 765 | * @param string $target target dir path 766 | * @param string $name file name 767 | * @return string|bool 768 | * @author Dmitry (dio) Levashov 769 | **/ 770 | protected function _move($source, $targetDir, $name) { 771 | $sql = 'UPDATE %s SET parent_id=%d, name="%s" WHERE id=%d LIMIT 1'; 772 | $sql = sprintf($sql, $this->tbf, $targetDir, $this->db->real_escape_string($name), $source); 773 | return $this->query($sql) && $this->db->affected_rows > 0 ? $source : false; 774 | } 775 | 776 | /** 777 | * Remove file 778 | * 779 | * @param string $path file path 780 | * @return bool 781 | * @author Dmitry (dio) Levashov 782 | **/ 783 | protected function _unlink($path) { 784 | return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime!="directory" LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows; 785 | } 786 | 787 | /** 788 | * Remove dir 789 | * 790 | * @param string $path dir path 791 | * @return bool 792 | * @author Dmitry (dio) Levashov 793 | **/ 794 | protected function _rmdir($path) { 795 | return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime="directory" LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows; 796 | } 797 | 798 | /** 799 | * undocumented function 800 | * 801 | * @return void 802 | * @author Dmitry Levashov 803 | **/ 804 | protected function _setContent($path, $fp) { 805 | rewind($fp); 806 | $fstat = fstat($fp); 807 | $size = $fstat['size']; 808 | 809 | 810 | } 811 | 812 | /** 813 | * Create new file and write into it from file pointer. 814 | * Return new file path or false on error. 815 | * 816 | * @param resource $fp file pointer 817 | * @param string $dir target dir path 818 | * @param string $name file name 819 | * @param array $stat file stat (required by some virtual fs) 820 | * @return bool|string 821 | * @author Dmitry (dio) Levashov 822 | **/ 823 | protected function _save($fp, $dir, $name, $stat) { 824 | $this->clearcache(); 825 | 826 | $mime = $stat['mime']; 827 | $w = !empty($stat['width']) ? $stat['width'] : 0; 828 | $h = !empty($stat['height']) ? $stat['height'] : 0; 829 | 830 | $id = $this->_joinPath($dir, $name); 831 | rewind($fp); 832 | $stat = fstat($fp); 833 | $size = $stat['size']; 834 | 835 | if (($tmpfile = tempnam($this->tmpPath, $this->id))) { 836 | if (($trgfp = fopen($tmpfile, 'wb')) == false) { 837 | unlink($tmpfile); 838 | } else { 839 | while (!feof($fp)) { 840 | fwrite($trgfp, fread($fp, 8192)); 841 | } 842 | fclose($trgfp); 843 | 844 | $sql = $id > 0 845 | ? '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)' 846 | : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, "%s", LOAD_FILE("%s"), %d, %d, "%s", %d, %d)'; 847 | $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->loadFilePath($tmpfile), $size, time(), $mime, $w, $h); 848 | 849 | $res = $this->query($sql); 850 | unlink($tmpfile); 851 | 852 | if ($res) { 853 | return $id > 0 ? $id : $this->db->insert_id; 854 | } 855 | } 856 | } 857 | 858 | 859 | $content = ''; 860 | rewind($fp); 861 | while (!feof($fp)) { 862 | $content .= fread($fp, 8192); 863 | } 864 | 865 | $sql = $id > 0 866 | ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, "%s", "%s", %d, %d, "%s", %d, %d)' 867 | : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, "%s", "%s", %d, %d, "%s", %d, %d)'; 868 | $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->db->real_escape_string($content), $size, time(), $mime, $w, $h); 869 | 870 | unset($content); 871 | 872 | if ($this->query($sql)) { 873 | return $id > 0 ? $id : $this->db->insert_id; 874 | } 875 | 876 | return false; 877 | } 878 | 879 | /** 880 | * Get file contents 881 | * 882 | * @param string $path file path 883 | * @return string|false 884 | * @author Dmitry (dio) Levashov 885 | **/ 886 | protected function _getContents($path) { 887 | return ($res = $this->query(sprintf('SELECT content FROM %s WHERE id=%d', $this->tbf, $path))) && ($r = $res->fetch_assoc()) ? $r['content'] : false; 888 | } 889 | 890 | /** 891 | * Write a string to a file 892 | * 893 | * @param string $path file path 894 | * @param string $content new file content 895 | * @return bool 896 | * @author Dmitry (dio) Levashov 897 | **/ 898 | protected function _filePutContents($path, $content) { 899 | 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)); 900 | } 901 | 902 | /** 903 | * Detect available archivers 904 | * 905 | * @return void 906 | **/ 907 | protected function _checkArchivers() { 908 | return; 909 | } 910 | 911 | /** 912 | * Unpack archive 913 | * 914 | * @param string $path archive path 915 | * @param array $arc archiver command and arguments (same as in $this->archivers) 916 | * @return void 917 | * @author Dmitry (dio) Levashov 918 | * @author Alexey Sukhotin 919 | **/ 920 | protected function _unpack($path, $arc) { 921 | return; 922 | } 923 | 924 | /** 925 | * Recursive symlinks search 926 | * 927 | * @param string $path file/dir path 928 | * @return bool 929 | * @author Dmitry (dio) Levashov 930 | **/ 931 | protected function _findSymlinks($path) { 932 | return false; 933 | } 934 | 935 | /** 936 | * Extract files from archive 937 | * 938 | * @param string $path archive path 939 | * @param array $arc archiver command and arguments (same as in $this->archivers) 940 | * @return true 941 | * @author Dmitry (dio) Levashov, 942 | * @author Alexey Sukhotin 943 | **/ 944 | protected function _extract($path, $arc) { 945 | return false; 946 | } 947 | 948 | /** 949 | * Create archive and return its path 950 | * 951 | * @param string $dir target dir 952 | * @param array $files files names list 953 | * @param string $name archive name 954 | * @param array $arc archiver options 955 | * @return string|bool 956 | * @author Dmitry (dio) Levashov, 957 | * @author Alexey Sukhotin 958 | **/ 959 | protected function _archive($dir, $files, $name, $arc) { 960 | return false; 961 | } 962 | 963 | } // END class 964 | -------------------------------------------------------------------------------- /src/Driver/ElFinderVolumeS3.php: -------------------------------------------------------------------------------- 1 | '', 30 | 'secretkey' => '', 31 | 'bucket' => '', 32 | ); 33 | $this->options = array_merge($this->options, $opts); 34 | $this->options['mimeDetect'] = 'internal'; 35 | 36 | } 37 | 38 | protected function init() { 39 | if (!$this->options['accesskey'] 40 | || !$this->options['secretkey'] 41 | || !$this->options['signature'] 42 | || !$this->options['region'] 43 | || !$this->options['bucket']) { 44 | return $this->setError('Required options undefined.'); 45 | } 46 | 47 | $this->s3 = S3Client::factory([ 48 | 'key' => $this->options['accesskey'], 49 | 'secret' => $this->options['secretkey'], 50 | 'signature' => $this->options['signature'], 51 | 'region' => $this->options['region'] 52 | ]); 53 | $this->s3->registerStreamWrapper(); 54 | 55 | $this->root = $this->options['path']; 56 | 57 | $this->rootName = 's3'; 58 | 59 | return true; 60 | } 61 | 62 | protected function configure() { 63 | parent::configure(); 64 | $this->mimeDetect = 'internal'; 65 | } 66 | 67 | /** 68 | * Return parent directory path 69 | * 70 | * @param string $path file path 71 | * @return string 72 | * @author Dmitry (dio) Levashov 73 | **/ 74 | protected function _dirname($path) { 75 | 76 | $newpath = preg_replace("/\/$/", "", $path); 77 | $dn = substr($path, 0, strrpos($newpath, '/')) ; 78 | 79 | if (substr($dn, 0, 1) != '/') { 80 | $dn = "/$dn"; 81 | } 82 | 83 | return $dn; 84 | } 85 | 86 | /** 87 | * Return file name 88 | * 89 | * @param string $path file path 90 | * @return string 91 | * @author Dmitry (dio) Levashov 92 | **/ 93 | protected function _basename($path) { 94 | return basename($path); 95 | } 96 | 97 | 98 | 99 | /** 100 | * Join dir name and file name and return full path. 101 | * Some drivers (db) use int as path - so we give to concat path to driver itself 102 | * 103 | * @param string $dir dir path 104 | * @param string $name file name 105 | * @return string 106 | * @author Dmitry (dio) Levashov 107 | **/ 108 | protected function _joinPath($dir, $name) { 109 | return $dir.DIRECTORY_SEPARATOR.$name; 110 | } 111 | 112 | /** 113 | * Return normalized path, this works the same as os.path.normpath() in Python 114 | * 115 | * @param string $path path 116 | * @return string 117 | * @author Troex Nevelin 118 | **/ 119 | protected function _normpath($path) { 120 | $tmp = preg_replace("/^\//", "", $path); 121 | $tmp = preg_replace("/\/\//", "/", $tmp); 122 | $tmp = preg_replace("/\/$/", "", $tmp); 123 | return $tmp; 124 | } 125 | 126 | /** 127 | * Return file path related to root dir 128 | * 129 | * @param string $path file path 130 | * @return string 131 | * @author Dmitry (dio) Levashov 132 | **/ 133 | protected function _relpath($path) { 134 | 135 | 136 | $newpath = $path; 137 | 138 | 139 | if (substr($path, 0, 1) != '/') { 140 | $newpath = "/$newpath"; 141 | } 142 | 143 | $newpath = preg_replace("/\/$/", "", $newpath); 144 | 145 | $ret = ($newpath == $this->root) ? '' : substr($newpath, strlen($this->root)+1); 146 | 147 | return $ret; 148 | } 149 | 150 | /** 151 | * Convert path related to root dir into real path 152 | * 153 | * @param string $path file path 154 | * @return string 155 | * @author Dmitry (dio) Levashov 156 | **/ 157 | protected function _abspath($path) { 158 | if ($path == $this->separator) { 159 | return $this->root; 160 | } else { 161 | $path = $this->root.$this->separator.$path; 162 | // Weird.. fixes "///" in paths. 163 | while (preg_match("/\/\//", $path)) { 164 | $path = preg_replace("/\/\//", "/", $path); 165 | } 166 | return $path; 167 | } 168 | } 169 | 170 | /** 171 | * Return fake path started from root dir 172 | * 173 | * @param string $path file path 174 | * @return string 175 | * @author Dmitry (dio) Levashov 176 | **/ 177 | protected function _path($path) { 178 | return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path)); 179 | } 180 | 181 | /** 182 | * Return true if $path is children of $parent 183 | * 184 | * @param string $path path to check 185 | * @param string $parent parent path 186 | * @return bool 187 | * @author Dmitry (dio) Levashov 188 | **/ 189 | protected function _inpath($path, $parent) { 190 | return $path == $parent || strpos($path, $parent.'/') === 0; 191 | } 192 | 193 | 194 | /** 195 | * Converting array of objects with name and value properties to 196 | * array[key] = value 197 | * @param array $metadata source array 198 | * @return array 199 | * @author Alexey Sukhotin 200 | **/ 201 | protected function metaobj2array($metadata) { 202 | $arr = array(); 203 | 204 | if (is_array($metadata)) { 205 | foreach ($metadata as $meta) { 206 | $arr[$meta->Name] = $meta->Value; 207 | } 208 | } else { 209 | $arr[$metadata->Name] = $metadata->Value; 210 | } 211 | return $arr; 212 | } 213 | 214 | /** 215 | * Return stat for given path. 216 | * Stat contains following fields: 217 | * - (int) size file size in b. required 218 | * - (int) ts file modification time in unix time. required 219 | * - (string) mime mimetype. required for folders, others - optionally 220 | * - (bool) read read permissions. required 221 | * - (bool) write write permissions. required 222 | * - (bool) locked is object locked. optionally 223 | * - (bool) hidden is object hidden. optionally 224 | * - (string) alias for symlinks - link target path relative to root path. optionally 225 | * - (string) target for symlinks - link target path. optionally 226 | * 227 | * If file does not exists - returns empty array or false. 228 | * 229 | * @param string $path file path 230 | * @return array|false 231 | * @author Dmitry (dio) Levashov, 232 | * @author Alexey Sukhotin 233 | **/ 234 | protected function _stat($path) { 235 | 236 | $stat = array( 237 | 'size' => 0, 238 | 'ts' => time(), 239 | 'read' => true, 240 | 'write' => true, 241 | 'locked' => false, 242 | 'hidden' => false, 243 | 'mime' => 'directory', 244 | ); 245 | 246 | // S3 apparently doesn't understand paths Key with a "/" at the end 247 | if (substr($path, -1) == "/") { 248 | $path = substr($path, 0, strlen($path) - 1); 249 | } 250 | 251 | if ($this->root == $path) { 252 | return $stat; 253 | } 254 | 255 | 256 | $np = $this->_normpath($path); 257 | /* @var $obj \Guzzle\Service\Resource\Model */ 258 | try { 259 | $obj = $this->s3->headObject(['Bucket' => $this->options['bucket'], 'Key' => $np]); 260 | } catch (NoSuchKeyException $e) { 261 | } 262 | 263 | if (!isset($obj)) { 264 | $np .= '/'; 265 | try { 266 | $obj = $this->s3->headObject(['Bucket' => $this->options['bucket'], 'Key' => $np]); 267 | } catch (NoSuchKeyException $e) { 268 | } 269 | } 270 | 271 | // No obj means it's a folder, or it really doesn't exist 272 | if (!isset($obj)) { 273 | if (!$this->_scandir($path)) { 274 | return false; 275 | } else { 276 | return $stat; 277 | } 278 | } 279 | 280 | $mime = ''; 281 | 282 | if ($obj->hasKey('Last-Modified')) { 283 | $stat['ts'] = strtotime($obj->get('Last-Modified')); 284 | } 285 | 286 | try { 287 | $files = $this->s3->listObjects(['Bucket' => $this->options['bucket'], 'Prefix' => $np, 'Delimiter' => '/'])->get('Contents'); 288 | } catch (Exception $e) { 289 | 290 | } 291 | 292 | $mime = $obj->get('ContentType'); 293 | $stat['mime'] = substr($np, -1) == '/' ? 'directory' : (!$mime ? 'text/plain' : $mime); 294 | foreach ($files as $file) { 295 | if ($file['Key'] == $np) { 296 | $stat['size'] = $file['Size']; 297 | } 298 | } 299 | 300 | return $stat; 301 | } 302 | 303 | 304 | 305 | /***************** file stat ********************/ 306 | 307 | 308 | /** 309 | * Return true if path is dir and has at least one childs directory 310 | * 311 | * @param string $path dir path 312 | * @return bool 313 | * @author Alexey Sukhotin 314 | **/ 315 | protected function _subdirs($path) { 316 | $stat = $this->_stat($path); 317 | 318 | if ($stat['mime'] == 'directory') { 319 | $files = $this->_scandir($path); 320 | foreach ($files as $file) { 321 | $fstat = $this->_stat($file); 322 | if ($fstat['mime'] == 'directory') { 323 | return true; 324 | } 325 | } 326 | 327 | } 328 | 329 | return false; 330 | } 331 | 332 | /** 333 | * Return object width and height 334 | * Ususaly used for images, but can be realize for video etc... 335 | * 336 | * @param string $path file path 337 | * @param string $mime file mime type 338 | * @return string 339 | * @author Dmitry (dio) Levashov 340 | **/ 341 | protected function _dimensions($path, $mime) { 342 | return false; 343 | } 344 | 345 | /******************** file/dir content *********************/ 346 | 347 | /** 348 | * Return files list in directory 349 | * 350 | * @param string $path dir path 351 | * @return array 352 | * @author Dmitry (dio) Levashov, 353 | * @author Alexey Sukhotin 354 | **/ 355 | protected function _scandir($path) { 356 | 357 | $s3path = preg_replace("/\/$/", "", $path); 358 | $s3path = preg_replace("/^\//", "", $s3path); 359 | 360 | $files = (array)$this->s3->listObjects(array('Bucket' => $this->options['bucket'], 'delimiter' => '/', 'Prefix' => $s3path))->get('Contents'); 361 | 362 | $finalfiles = array(); 363 | $folders = array(); 364 | foreach ($files as $file) { 365 | if (preg_match("|^" . preg_replace("/^\//", "", $s3path) . '/' . "[^/]*/?$|", $file['Key'])) { 366 | $fname = $file['Key']; 367 | if (!$fname || $fname == preg_replace("/\/$/", "", $s3path) || $fname == preg_replace("/$/", "/", $s3path)) { 368 | continue; 369 | } 370 | $finalfiles[] = preg_replace("/\/$/", "", $fname); 371 | } else { 372 | $matches = array(); 373 | if ($res = preg_match("|^" . preg_replace("/^\//", "", $s3path) . '/' . "(.*?)\/|", $file['Key'], $matches)) { 374 | $folders[$matches[1]] = true; 375 | } 376 | } 377 | } 378 | 379 | // Folders retrieved differently, as it's not a real object on S3 380 | foreach ($folders as $forlderName => $tmp) { 381 | if (!in_array(preg_replace("/^\//", "", $s3path)."/".$forlderName, $finalfiles)) { 382 | $finalfiles[] = preg_replace("/^\//", "", $s3path)."/".$forlderName; 383 | } 384 | } 385 | 386 | sort($finalfiles); 387 | return $finalfiles; 388 | } 389 | 390 | /** 391 | * Return temporary file path for required file 392 | * 393 | * @param string $path file path 394 | * @return string 395 | * @author Dmitry (dio) Levashov 396 | **/ 397 | protected function tmpname($path) { 398 | return $this->tmpPath.DIRECTORY_SEPARATOR.md5($path); 399 | } 400 | 401 | /** 402 | * Open file and return file pointer 403 | * 404 | * @param string $path file path 405 | * @param bool $write open file for writing 406 | * @return resource|false 407 | * @author Dmitry (dio) Levashov, 408 | * @author Alexey Sukhotin 409 | **/ 410 | protected function _fopen($path, $mode="rb") { 411 | return fopen('s3://'.$this->options['bucket'].'/'.$this->_normpath($path), $mode); 412 | } 413 | 414 | /** 415 | * Close opened file 416 | * 417 | * @param resource $fp file pointer 418 | * @param string $path file path 419 | * @return bool 420 | * @author Dmitry (dio) Levashov 421 | **/ 422 | protected function _fclose($fp, $path='') { 423 | @fclose($fp); 424 | if ($path) { 425 | @unlink($this->tmpname($path)); 426 | } 427 | } 428 | 429 | /******************** file/dir manipulations *************************/ 430 | 431 | /** 432 | * Create dir and return created dir path or false on failed 433 | * 434 | * @param string $path parent dir path 435 | * @param string $name new directory name 436 | * @return string|bool 437 | * @author Dmitry (dio) Levashov, 438 | * @author Alexey Sukhotin 439 | **/ 440 | protected function _mkdir($path, $name) { 441 | 442 | $newkey = $this->_normpath($path); 443 | $newkey = preg_replace("/\/$/", "", $newkey); 444 | $newkey = "$newkey/$name/"; 445 | 446 | try { 447 | mkdir('s3://'.$this->options['bucket'].'/'.$newkey); 448 | return "$path/$name"; 449 | } catch (Exception $e) { 450 | return false; 451 | } 452 | } 453 | 454 | /** 455 | * Create file and return it's path or false on failed 456 | * 457 | * @param string $path parent dir path 458 | * @param string $name new file name 459 | * @return string|bool 460 | * @author Dmitry (dio) Levashov, 461 | * @author Alexey Sukhotin 462 | **/ 463 | protected function _mkfile($path, $name) { 464 | $newkey = $this->_normpath($path); 465 | $newkey = preg_replace("/\/$/", "", $newkey); 466 | $newkey = "$newkey/$name"; 467 | 468 | try { 469 | touch('s3://'.$this->options['bucket'].'/'.$newkey, null, null, stream_context_create([ 470 | 's3' => array('ACL' => CannedAcl::PUBLIC_READ) 471 | ])); 472 | } catch (Exception $e) { 473 | 474 | } 475 | 476 | if (isset($obj)) { 477 | return "$path/$name"; 478 | } 479 | 480 | return false; 481 | 482 | } 483 | 484 | /** 485 | * Create symlink 486 | * 487 | * @param string $source file to link to 488 | * @param string $targetDir folder to create link in 489 | * @param string $name symlink name 490 | * @return bool 491 | * @author Dmitry (dio) Levashov 492 | **/ 493 | protected function _symlink($source, $targetDir, $name) { 494 | return false; 495 | } 496 | 497 | /** 498 | * Copy file into another file (only inside one volume) 499 | * 500 | * @param string $source source file path 501 | * @param string $target target dir path 502 | * @param string $name file name 503 | * @return bool 504 | * @author Dmitry (dio) Levashov 505 | **/ 506 | protected function _copy($source, $targetDir, $name) { 507 | $sourcekey = $this->_normpath($source); 508 | $sourcekey = preg_replace("/\/$/", "", $sourcekey); 509 | $newkey = $this->_normpath($targetDir.'/'.$name); 510 | $newkey = preg_replace("/\/$/", "", $newkey); 511 | 512 | copy('s3://'.$this->options['bucket'].'/'.$sourcekey, 's3://'.$this->options['bucket'].'/'.$newkey, stream_context_create([ 513 | 's3' => ['ACL' => CannedAcl::PUBLIC_READ] 514 | ])); 515 | return true; 516 | } 517 | 518 | /** 519 | * Move file into another parent dir. 520 | * Return new file path or false. 521 | * 522 | * @param string $source source file path 523 | * @param string $target target dir path 524 | * @param string $name file name 525 | * @return string|bool 526 | * @author Dmitry (dio) Levashov 527 | **/ 528 | protected function _move($source, $targetDir, $name) { 529 | $this->_copy($source, $targetDir, $name); 530 | $newkey = $this->_normpath($source); 531 | $newkey = preg_replace("/\/$/", "", $newkey); 532 | unlink('s3://'.$this->options['bucket'].'/'.$newkey); 533 | } 534 | 535 | /** 536 | * Remove file 537 | * 538 | * @param string $path file path 539 | * @return bool 540 | * @author Dmitry (dio) Levashov 541 | **/ 542 | protected function _unlink($path) { 543 | 544 | $newkey = $this->_normpath($path); 545 | $newkey = preg_replace("/\/$/", "", $newkey); 546 | 547 | try { 548 | $obj = $this->s3->deleteObject(array('Bucket' => $this->options['bucket'], 'Key' => $newkey)); 549 | return true; 550 | } catch (Exception $e) { 551 | 552 | } 553 | return false; 554 | } 555 | 556 | /** 557 | * Remove dir 558 | * 559 | * @param string $path dir path 560 | * @return bool 561 | * @author Dmitry (dio) Levashov 562 | **/ 563 | protected function _rmdir($path) { 564 | $newkey = $this->_normpath($path).'/'; 565 | 566 | try { 567 | $obj = $this->s3->deleteObject(array('Bucket' => $this->options['bucket'], 'Key' => $newkey)); 568 | return true; 569 | } catch (Exception $e) { 570 | 571 | } 572 | return false; 573 | } 574 | 575 | /** 576 | * Create new file and write into it from file pointer. 577 | * Return new file path or false on error. 578 | * 579 | * @param resource $fp file pointer 580 | * @param string $dir target dir path 581 | * @param string $name file name 582 | * @return bool|string 583 | * @author Dmitry (dio) Levashov 584 | **/ 585 | protected function _save($fp, $dir, $name, $stat) { 586 | $contents = stream_get_contents($fp); 587 | fclose($fp); 588 | $this->_filePutContents($dir.'/'.$name, $contents); 589 | return $dir.'/'.$name; 590 | } 591 | 592 | /** 593 | * Get file contents 594 | * 595 | * @param string $path file path 596 | * @return string|false 597 | * @author Dmitry (dio) Levashov 598 | **/ 599 | protected function _getContents($path) { 600 | $newkey = $this->_normpath($path); 601 | $newkey = preg_replace("/\/$/", "", $newkey); 602 | return file_get_contents('s3://'.$this->options['bucket'].'/'.$newkey); 603 | } 604 | 605 | /** 606 | * Write a string to a file 607 | * 608 | * @param string $path file path 609 | * @param string $content new file content 610 | * @return bool 611 | * @author Dmitry (dio) Levashov 612 | **/ 613 | protected function _filePutContents($path, $content) { 614 | $newkey = $this->_normpath($path); 615 | $newkey = preg_replace("/\/$/", "", $newkey); 616 | 617 | $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); 618 | 619 | $this->s3->putObject([ 620 | 'Bucket' => $this->options['bucket'], 621 | 'Key' => $newkey, 622 | 'Body' => $content, 623 | 'ACL' => CannedAcl::PUBLIC_READ, 624 | 'ContentType' => self::$mimetypes[$ext] 625 | ]); 626 | return true; 627 | } 628 | 629 | /** 630 | * Extract files from archive 631 | * 632 | * @param string $path file path 633 | * @param array $arc archiver options 634 | * @return bool 635 | * @author Dmitry (dio) Levashov, 636 | * @author Alexey Sukhotin 637 | **/ 638 | protected function _extract($path, $arc) { 639 | return false; 640 | } 641 | 642 | /** 643 | * Create archive and return its path 644 | * 645 | * @param string $dir target dir 646 | * @param array $files files names list 647 | * @param string $name archive name 648 | * @param array $arc archiver options 649 | * @return string|bool 650 | * @author Dmitry (dio) Levashov, 651 | * @author Alexey Sukhotin 652 | **/ 653 | protected function _archive($dir, $files, $name, $arc) { 654 | return false; 655 | } 656 | 657 | /** 658 | * Detect available archivers 659 | * 660 | * @return void 661 | * @author Dmitry (dio) Levashov, 662 | * @author Alexey Sukhotin 663 | **/ 664 | protected function _checkArchivers() { 665 | 666 | } 667 | 668 | /** 669 | * chmod implementation 670 | * 671 | * @return bool 672 | **/ 673 | protected function _chmod($path, $mode) { 674 | return false; 675 | } 676 | } 677 | 678 | -------------------------------------------------------------------------------- /src/Plugins/AutoResize/plugin.php: -------------------------------------------------------------------------------- 1 | true, // For control by volume driver 10 | 'maxWidth' => 1024, // Path to Water mark image 11 | 'maxHeight' => 1024, // Margin right pixel 12 | 'quality' => 95, // JPEG image save quality 13 | 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP // Target image formats ( bit-field ) 14 | ); 15 | 16 | $this->opts = array_merge($defaults, $opts); 17 | 18 | } 19 | 20 | public function onUpLoadPreSave(&$path, &$name, $src, $elfinder, $volume) { 21 | $opts = $this->opts; 22 | $volOpts = $volume->getOptionsPlugin('AutoResize'); 23 | if (is_array($volOpts)) { 24 | $opts = array_merge($this->opts, $volOpts); 25 | } 26 | 27 | if (! $opts['enable']) { 28 | return false; 29 | } 30 | 31 | $srcImgInfo = @getimagesize($src); 32 | if ($srcImgInfo === false) { 33 | return false; 34 | } 35 | 36 | // check target image type 37 | $imgTypes = array( 38 | IMAGETYPE_GIF => IMG_GIF, 39 | IMAGETYPE_JPEG => IMG_JPEG, 40 | IMAGETYPE_PNG => IMG_PNG, 41 | IMAGETYPE_WBMP => IMG_WBMP, 42 | ); 43 | if (! ($opts['targetType'] & $imgTypes[$srcImgInfo[2]])) { 44 | return false; 45 | } 46 | 47 | if ($srcImgInfo[0] > $opts['maxWidth'] || $srcImgInfo[1] > $opts['maxHeight']) { 48 | return $this->resize($src, $srcImgInfo, $opts['maxWidth'], $opts['maxHeight'], $opts['quality']); 49 | } 50 | 51 | return false; 52 | } 53 | 54 | private function resize($src, $srcImgInfo, $maxWidth, $maxHeight, $quality) { 55 | $zoom = min(($maxWidth/$srcImgInfo[0]),($maxHeight/$srcImgInfo[1])); 56 | $width = round($srcImgInfo[0] * $zoom); 57 | $height = round($srcImgInfo[1] * $zoom); 58 | 59 | if (class_exists('Imagick')) { 60 | return $this->resize_imagick($src, $width, $height, $quality); 61 | } else { 62 | return $this->resize_gd($src, $width, $height, $quality, $srcImgInfo); 63 | } 64 | } 65 | 66 | private function resize_gd($src, $width, $height, $quality, $srcImgInfo) { 67 | switch ($srcImgInfo['mime']) { 68 | case 'image/gif': 69 | if (@imagetypes() & IMG_GIF) { 70 | $oSrcImg = @imagecreatefromgif($src); 71 | } else { 72 | $ermsg = 'GIF images are not supported'; 73 | } 74 | break; 75 | case 'image/jpeg': 76 | if (@imagetypes() & IMG_JPG) { 77 | $oSrcImg = @imagecreatefromjpeg($src) ; 78 | } else { 79 | $ermsg = 'JPEG images are not supported'; 80 | } 81 | break; 82 | case 'image/png': 83 | if (@imagetypes() & IMG_PNG) { 84 | $oSrcImg = @imagecreatefrompng($src) ; 85 | } else { 86 | $ermsg = 'PNG images are not supported'; 87 | } 88 | break; 89 | case 'image/wbmp': 90 | if (@imagetypes() & IMG_WBMP) { 91 | $oSrcImg = @imagecreatefromwbmp($src); 92 | } else { 93 | $ermsg = 'WBMP images are not supported'; 94 | } 95 | break; 96 | default: 97 | $oSrcImg = false; 98 | $ermsg = $srcImgInfo['mime'].' images are not supported'; 99 | break; 100 | } 101 | 102 | if ($oSrcImg && false != ($tmp = imagecreatetruecolor($width, $height))) { 103 | 104 | if (!imagecopyresampled($tmp, $oSrcImg, 0, 0, 0, 0, $width, $height, $srcImgInfo[0], $srcImgInfo[1])) { 105 | return false; 106 | } 107 | 108 | switch ($srcImgInfo['mime']) { 109 | case 'image/gif': 110 | imagegif($tmp, $src); 111 | break; 112 | case 'image/jpeg': 113 | imagejpeg($tmp, $src, $quality); 114 | break; 115 | case 'image/png': 116 | if (function_exists('imagesavealpha') && function_exists('imagealphablending')) { 117 | imagealphablending($tmp, false); 118 | imagesavealpha($tmp, true); 119 | } 120 | imagepng($tmp, $src); 121 | break; 122 | case 'image/wbmp': 123 | imagewbmp($tmp, $src); 124 | break; 125 | } 126 | 127 | imagedestroy($oSrcImg); 128 | imagedestroy($tmp); 129 | 130 | return true; 131 | 132 | } 133 | return false; 134 | } 135 | 136 | private function resize_imagick($src, $width, $height, $quality) { 137 | try { 138 | $img = new imagick($src); 139 | 140 | if (strtoupper($img->getImageFormat()) === 'JPEG') { 141 | $img->setImageCompression(imagick::COMPRESSION_JPEG); 142 | $img->setCompressionQuality($quality); 143 | } 144 | 145 | $img->resizeImage($width, $height, Imagick::FILTER_LANCZOS, true); 146 | 147 | $result = $img->writeImage($src); 148 | 149 | $img->clear(); 150 | $img->destroy(); 151 | 152 | return $result ? true : false; 153 | } catch (Exception $e) { 154 | return false; 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Plugins/Normalizer/plugin.php: -------------------------------------------------------------------------------- 1 | = 5.3.0, PECL intl >= 1.0.0) 10 | * or PEAR package "I18N_UnicodeNormalizer" 11 | * 12 | * ex. binding, configure on connector options 13 | * $opts = array( 14 | * 'bind' => array( 15 | * 'mkdir.pre mkfile.pre rename.pre' => array( 16 | * 'Plugin.Normalizer.cmdPreprocess' 17 | * ), 18 | * 'upload.presave' => array( 19 | * 'Plugin.Normalizer.onUpLoadPreSave' 20 | * ) 21 | * ), 22 | * // global configure (optional) 23 | * 'plugin' => array( 24 | * 'Normalizer' => array( 25 | * 'enable' => true, 26 | * 'nfc' => true, 27 | * 'nfkc' => true, 28 | * 'lowercase' => false, 29 | * 'convmap' => array() 30 | * ) 31 | * ), 32 | * // each volume configure (optional) 33 | * 'roots' => array( 34 | * array( 35 | * 'driver' => 'LocalFileSystem', 36 | * 'path' => '/path/to/files/', 37 | * 'URL' => 'http://localhost/to/files/' 38 | * 'plugin' => array( 39 | * 'Normalizer' => array( 40 | * 'enable' => true, 41 | * 'nfc' => true, 42 | * 'nfkc' => true, 43 | * 'lowercase' => false, 44 | * 'convmap' => array() 45 | * ) 46 | * ) 47 | * ) 48 | * ) 49 | * ); 50 | * 51 | * @package elfinder 52 | * @author Naoki Sawada 53 | * @license New BSD 54 | */ 55 | class elFinderPluginNormalizer 56 | { 57 | private $opts = array(); 58 | 59 | public function __construct($opts) { 60 | $defaults = array( 61 | 'enable' => true, // For control by volume driver 62 | 'nfc' => true, // Canonical Decomposition followed by Canonical Composition 63 | 'nfkc' => true, // Compatibility Decomposition followed by Canonical 64 | 'lowercase' => false, // Make chars lowercase 65 | 'convmap' => array() // Convert map ('FROM' => 'TO') array 66 | ); 67 | 68 | $this->opts = array_merge($defaults, $opts); 69 | } 70 | 71 | public function cmdPreprocess($cmd, &$args, $elfinder, $volume) { 72 | $opts = $this->getOpts($volume); 73 | if (! $opts['enable']) { 74 | return false; 75 | } 76 | 77 | if (isset($args['name'])) { 78 | $args['name'] = $this->normalize($args['name'], $opts); 79 | } 80 | return true; 81 | } 82 | 83 | public function onUpLoadPreSave(&$path, &$name, $src, $elfinder, $volume) { 84 | $opts = $this->getOpts($volume); 85 | if (! $opts['enable']) { 86 | return false; 87 | } 88 | 89 | if ($path) { 90 | $path = $this->normalize($path, $opts); 91 | } 92 | $name = $this->normalize($name, $opts); 93 | return true; 94 | } 95 | 96 | private function getOpts($volume) { 97 | $opts = $this->opts; 98 | if (is_object($volume)) { 99 | $volOpts = $volume->getOptionsPlugin('Normalizer'); 100 | if (is_array($volOpts)) { 101 | $opts = array_merge($this->opts, $volOpts); 102 | } 103 | } 104 | return $opts; 105 | } 106 | 107 | private function normalize($str, $opts) { 108 | if (class_exists('Normalizer')) { 109 | if ($opts['nfc'] && ! Normalizer::isNormalized($str, Normalizer::FORM_C)) 110 | $str = Normalizer::normalize($str, Normalizer::FORM_C); 111 | if ($opts['nfkc'] && ! Normalizer::isNormalized($str, Normalizer::FORM_KC)) 112 | $str = Normalizer::normalize($str, Normalizer::FORM_KC); 113 | } else { 114 | if (! class_exists('I18N_UnicodeNormalizer')) { 115 | @ include_once 'I18N/UnicodeNormalizer.php'; 116 | } 117 | if (class_exists('I18N_UnicodeNormalizer')) { 118 | $normalizer = new I18N_UnicodeNormalizer(); 119 | if ($opts['nfc']) 120 | $str = $normalizer->normalize($str, 'NFC'); 121 | if ($opts['nfkc']) 122 | $str = $normalizer->normalize($str, 'NFKC'); 123 | } 124 | } 125 | if ($opts['convmap'] && is_array($opts['convmap'])) { 126 | $str = strtr($str, $opts['convmap']); 127 | } 128 | if ($opts['lowercase']) { 129 | if (function_exists('mb_strtolower')) { 130 | $str = mb_strtolower($str, 'UTF-8'); 131 | } else { 132 | $str = strtolower($str); 133 | } 134 | } 135 | return $str; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/Plugins/Sanitizer/plugin.php: -------------------------------------------------------------------------------- 1 | array( 10 | * 'mkdir.pre mkfile.pre rename.pre' => array( 11 | * 'Plugin.Sanitizer.cmdPreprocess' 12 | * ), 13 | * 'upload.presave' => array( 14 | * 'Plugin.Sanitizer.onUpLoadPreSave' 15 | * ) 16 | * ), 17 | * // global configure (optional) 18 | * 'plugin' => array( 19 | * 'Sanitizer' => array( 20 | * 'enable' => true, 21 | * 'targets' => array('\\','/',':','*','?','"','<','>','|'), // target chars 22 | * 'replace' => '_' // replace to this 23 | * ) 24 | * ), 25 | * // each volume configure (optional) 26 | * 'roots' => array( 27 | * array( 28 | * 'driver' => 'LocalFileSystem', 29 | * 'path' => '/path/to/files/', 30 | * 'URL' => 'http://localhost/to/files/' 31 | * 'plugin' => array( 32 | * 'Sanitizer' => array( 33 | * 'enable' => true, 34 | * 'targets' => array('\\','/',':','*','?','"','<','>','|'), // target chars 35 | * 'replace' => '_' // replace to this 36 | * ) 37 | * ) 38 | * ) 39 | * ) 40 | * ); 41 | * 42 | * @package elfinder 43 | * @author Naoki Sawada 44 | * @license New BSD 45 | */ 46 | class elFinderPluginSanitizer 47 | { 48 | private $opts = array(); 49 | 50 | public function __construct($opts) { 51 | $defaults = array( 52 | 'enable' => true, // For control by volume driver 53 | 'targets' => array('\\','/',':','*','?','"','<','>','|'), // target chars 54 | 'replace' => '_' // replace to this 55 | ); 56 | 57 | $this->opts = array_merge($defaults, $opts); 58 | } 59 | 60 | public function cmdPreprocess($cmd, &$args, $elfinder, $volume) { 61 | $opts = $this->getOpts($volume); 62 | if (! $opts['enable']) { 63 | return false; 64 | } 65 | 66 | if (isset($args['name'])) { 67 | $args['name'] = $this->sanitizeFileName($args['name'], $opts); 68 | } 69 | return true; 70 | } 71 | 72 | public function onUpLoadPreSave(&$path, &$name, $src, $elfinder, $volume) { 73 | $opts = $this->getOpts($volume); 74 | if (! $opts['enable']) { 75 | return false; 76 | } 77 | 78 | if ($path) { 79 | $path = $this->sanitizeFileName($path, $opts, array('/')); 80 | } 81 | $name = $this->sanitizeFileName($name, $opts); 82 | return true; 83 | } 84 | 85 | private function getOpts($volume) { 86 | $opts = $this->opts; 87 | if (is_object($volume)) { 88 | $volOpts = $volume->getOptionsPlugin('Sanitizer'); 89 | if (is_array($volOpts)) { 90 | $opts = array_merge($this->opts, $volOpts); 91 | } 92 | } 93 | return $opts; 94 | } 95 | 96 | private function sanitizeFileName($filename, $opts, $allows = array()) { 97 | $targets = $allows? array_diff($opts['targets'], $allows) : $opts['targets']; 98 | return str_replace($targets, $opts['replace'], $filename); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Plugins/Thumbnails/plugin.php: -------------------------------------------------------------------------------- 1 | true, 10 | 'thumb_path' => '', 11 | 'thumb' => '' 12 | ]; 13 | 14 | public function __construct($options) 15 | { 16 | $this->options = array_merge( $this->defaultOptions, $options ); 17 | } 18 | 19 | public function generateThumbs(&$path, &$name, $src, $elfinder, $volume) 20 | { 21 | 22 | $options = $this->pluginEnabled($volume); 23 | if($options == 'false') 24 | { 25 | return false; 26 | } 27 | 28 | $imgTypes = $this->mimeType($options, $src); 29 | if($imgTypes == 'false') 30 | { 31 | return false; 32 | } 33 | 34 | $thumbPath = $path.$options['thumb_path']; 35 | 36 | // create Dirs if not exist 37 | // todo: switch to function, create 5 folders, hide folders, check settings 38 | $this->createFolders($thumbPath); 39 | 40 | 41 | $manager = new ImageManager(array('driver' => 'gd')); 42 | $this->resize($src, $options, $manager, $thumbPath, $name); 43 | 44 | } 45 | 46 | protected function mimeType($opts, $src) 47 | { 48 | 49 | $srcImgInfo = @getimagesize( $src ); 50 | if ( $srcImgInfo === false ) { 51 | return 'false'; 52 | } 53 | 54 | switch ( $srcImgInfo[ 'mime' ] ) { 55 | case 'image/gif': 56 | break; 57 | case 'image/jpeg': 58 | break; 59 | case 'image/png': 60 | break; 61 | 62 | default: 63 | return 'false'; 64 | } 65 | 66 | } 67 | private function pluginEnabled($volume) 68 | { 69 | $defaultOptions = $this->options; 70 | $configOptions = $volume->getOptionsPlugin('Thumbnails'); 71 | 72 | if (is_array($configOptions)) { 73 | $options = array_merge($this->defaultOptions, $configOptions); 74 | $this->options = $options; 75 | } 76 | 77 | if (! $options['enable']) { 78 | return 'false'; 79 | } 80 | 81 | return $options; 82 | } 83 | 84 | private function createFolders($thumbPath) 85 | { 86 | $thumbs = $this->options['thumb']; 87 | $thumbs = explode('|', $thumbs); 88 | 89 | foreach( $thumbs as $key => $value) 90 | { 91 | if($value != '') 92 | { 93 | if( ! is_dir( $thumbPath . '.thumb' . $key )) 94 | { 95 | mkdir($thumbPath . '.thumb' . $key, 0777, true); 96 | } 97 | } 98 | } 99 | } 100 | 101 | private function resize($src, $options, $manager, $thumbPath, $name) 102 | { 103 | $thumbs = $this->options['thumb']; 104 | $thumbs = explode('|', $thumbs); 105 | 106 | foreach( $thumbs as $key => $value) 107 | { 108 | if($value != '') 109 | { 110 | // to finally create image instances 111 | $image = $manager->make( $src ); 112 | // prevent possible upsizing 113 | $image->resize( $value , null, function( $constraint ) { 114 | $constraint->aspectRatio(); 115 | $constraint->upsize(); 116 | } ); 117 | 118 | $image->save($thumbPath . '.thumb' . $key . '/' . $name); 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Plugins/Watermark/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helios-ag/ElFinderPHP/d6b28121fd7aba089c02c5638cdda6466f760ffa/src/Plugins/Watermark/logo.png -------------------------------------------------------------------------------- /src/Plugins/Watermark/plugin.php: -------------------------------------------------------------------------------- 1 | true, // For control by volume driver 11 | 'source' => 'logo.png', // Path to Water mark image 12 | 'marginRight' => 5, // Margin right pixel 13 | 'marginBottom' => 5, // Margin bottom pixel 14 | 'quality' => 95, // JPEG image save quality 15 | 'transparency' => 70, // Water mark image transparency ( other than PNG ) 16 | 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) 17 | 'targetMinPixel' => 200 // Target image minimum pixel size 18 | ); 19 | 20 | $this->opts = array_merge($defaults, $opts); 21 | 22 | } 23 | 24 | public function onUpLoadPreSave(&$path, &$name, $src, $elfinder, $volume) { 25 | 26 | $opts = $this->opts; 27 | $volOpts = $volume->getOptionsPlugin('Watermark'); 28 | if (is_array($volOpts)) { 29 | $opts = array_merge($this->opts, $volOpts); 30 | } 31 | 32 | if (! $opts['enable']) { 33 | return false; 34 | } 35 | 36 | $srcImgInfo = @getimagesize($src); 37 | if ($srcImgInfo === false) { 38 | return false; 39 | } 40 | 41 | // check water mark image 42 | if (! file_exists($opts['source'])) { 43 | $opts['source'] = dirname(__FILE__) . "/" . $opts['source']; 44 | } 45 | if (is_readable($opts['source'])) { 46 | $watermarkImgInfo = @getimagesize($opts['source']); 47 | if (! $watermarkImgInfo) { 48 | return false; 49 | } 50 | } else { 51 | return false; 52 | } 53 | 54 | $watermark = $opts['source']; 55 | $marginLeft = $opts['marginRight']; 56 | $marginBottom = $opts['marginBottom']; 57 | $quality = $opts['quality']; 58 | $transparency = $opts['transparency']; 59 | 60 | // check target image type 61 | $imgTypes = array( 62 | IMAGETYPE_GIF => IMG_GIF, 63 | IMAGETYPE_JPEG => IMG_JPEG, 64 | IMAGETYPE_PNG => IMG_PNG, 65 | IMAGETYPE_WBMP => IMG_WBMP, 66 | ); 67 | if (! ($opts['targetType'] & $imgTypes[$srcImgInfo[2]])) { 68 | return false; 69 | } 70 | 71 | // check target image size 72 | if ($opts['targetMinPixel'] > 0 && $opts['targetMinPixel'] > min($srcImgInfo[0], $srcImgInfo[1])) { 73 | return false; 74 | } 75 | 76 | $watermark_width = $watermarkImgInfo[0]; 77 | $watermark_height = $watermarkImgInfo[1]; 78 | $dest_x = $srcImgInfo[0] - $watermark_width - $marginLeft; 79 | $dest_y = $srcImgInfo[1] - $watermark_height - $marginBottom; 80 | 81 | if (class_exists('Imagick')) { 82 | return $this->watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo); 83 | } else { 84 | return $this->watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo); 85 | } 86 | } 87 | 88 | private function watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo) { 89 | 90 | try { 91 | // Open the original image 92 | $img = new Imagick($src); 93 | 94 | // Open the watermark 95 | $watermark = new Imagick($watermark); 96 | 97 | // Set transparency 98 | if (strtoupper($watermark->getImageFormat()) !== 'PNG') { 99 | $watermark->setImageOpacity($transparency/100); 100 | } 101 | 102 | // Overlay the watermark on the original image 103 | $img->compositeImage($watermark, imagick::COMPOSITE_OVER, $dest_x, $dest_y); 104 | 105 | // Set quality 106 | if (strtoupper($img->getImageFormat()) === 'JPEG') { 107 | $img->setImageCompression(imagick::COMPRESSION_JPEG); 108 | $img->setCompressionQuality($quality); 109 | } 110 | 111 | $result = $img->writeImage($src); 112 | 113 | $img->clear(); 114 | $img->destroy(); 115 | $watermark->clear(); 116 | $watermark->destroy(); 117 | 118 | return $result ? true : false; 119 | } catch (Exception $e) { 120 | return false; 121 | } 122 | } 123 | 124 | private function watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo) { 125 | 126 | $watermark_width = $watermarkImgInfo[0]; 127 | $watermark_height = $watermarkImgInfo[1]; 128 | 129 | $ermsg = ''; 130 | switch ($watermarkImgInfo['mime']) { 131 | case 'image/gif': 132 | if (@imagetypes() & IMG_GIF) { 133 | $oWatermarkImg = @imagecreatefromgif($watermark); 134 | } else { 135 | $ermsg = 'GIF images are not supported'; 136 | } 137 | break; 138 | case 'image/jpeg': 139 | if (@imagetypes() & IMG_JPG) { 140 | $oWatermarkImg = @imagecreatefromjpeg($watermark) ; 141 | } else { 142 | $ermsg = 'JPEG images are not supported'; 143 | } 144 | break; 145 | case 'image/png': 146 | if (@imagetypes() & IMG_PNG) { 147 | $oWatermarkImg = @imagecreatefrompng($watermark) ; 148 | } else { 149 | $ermsg = 'PNG images are not supported'; 150 | } 151 | break; 152 | case 'image/wbmp': 153 | if (@imagetypes() & IMG_WBMP) { 154 | $oWatermarkImg = @imagecreatefromwbmp($watermark); 155 | } else { 156 | $ermsg = 'WBMP images are not supported'; 157 | } 158 | break; 159 | default: 160 | $oWatermarkImg = false; 161 | $ermsg = $watermarkImgInfo['mime'].' images are not supported'; 162 | break; 163 | } 164 | 165 | if (! $ermsg) { 166 | switch ($srcImgInfo['mime']) { 167 | case 'image/gif': 168 | if (@imagetypes() & IMG_GIF) { 169 | $oSrcImg = @imagecreatefromgif($src); 170 | } else { 171 | $ermsg = 'GIF images are not supported'; 172 | } 173 | break; 174 | case 'image/jpeg': 175 | if (@imagetypes() & IMG_JPG) { 176 | $oSrcImg = @imagecreatefromjpeg($src) ; 177 | } else { 178 | $ermsg = 'JPEG images are not supported'; 179 | } 180 | break; 181 | case 'image/png': 182 | if (@imagetypes() & IMG_PNG) { 183 | $oSrcImg = @imagecreatefrompng($src) ; 184 | } else { 185 | $ermsg = 'PNG images are not supported'; 186 | } 187 | break; 188 | case 'image/wbmp': 189 | if (@imagetypes() & IMG_WBMP) { 190 | $oSrcImg = @imagecreatefromwbmp($src); 191 | } else { 192 | $ermsg = 'WBMP images are not supported'; 193 | } 194 | break; 195 | default: 196 | $oSrcImg = false; 197 | $ermsg = $srcImgInfo['mime'].' images are not supported'; 198 | break; 199 | } 200 | } 201 | 202 | if ($ermsg || false === $oSrcImg || false === $oWatermarkImg) { 203 | return false; 204 | } 205 | 206 | if ($srcImgInfo['mime'] === 'image/png') { 207 | if (function_exists('imagecolorallocatealpha')) { 208 | $bg = imagecolorallocatealpha($oSrcImg, 255, 255, 255, 127); 209 | imagefill($oSrcImg, 0, 0 , $bg); 210 | } 211 | } 212 | 213 | if ($watermarkImgInfo['mime'] === 'image/png') { 214 | imagecopy($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height); 215 | } else { 216 | imagecopymerge($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $transparency); 217 | } 218 | 219 | switch ($srcImgInfo['mime']) { 220 | case 'image/gif': 221 | imagegif($oSrcImg, $src); 222 | break; 223 | case 'image/jpeg': 224 | imagejpeg($oSrcImg, $src, $quality); 225 | break; 226 | case 'image/png': 227 | if (function_exists('imagesavealpha') && function_exists('imagealphablending')) { 228 | imagealphablending($oSrcImg, false); 229 | imagesavealpha($oSrcImg, true); 230 | } 231 | imagepng($oSrcImg, $src); 232 | break; 233 | case 'image/wbmp': 234 | imagewbmp($oSrcImg, $src); 235 | break; 236 | } 237 | 238 | imageDestroy($oSrcImg); 239 | imageDestroy($oWatermarkImg); 240 | 241 | return true; 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/Utility/GetUrl.php: -------------------------------------------------------------------------------- 1 | adapter = $filesystem->getAdapter(); 40 | 41 | // For a cached adapter, get the underlying instance 42 | if ($this->adapter instanceof CachedAdapter) { 43 | $this->adapter = $this->adapter->getAdapter(); 44 | } 45 | 46 | //TODO: Check on actual implementations, not just an existing method 47 | $this->hasMethod = method_exists($this->adapter, 'getUrl'); 48 | } 49 | 50 | } 51 | 52 | /** 53 | * Get the method name. 54 | * 55 | * @return string 56 | */ 57 | public function getMethod() 58 | { 59 | return 'getUrl'; 60 | } 61 | 62 | /** 63 | * Get the public url 64 | * 65 | * @param string $path path to file 66 | * 67 | * @return string|false 68 | */ 69 | public function handle($path = null) 70 | { 71 | if (is_null($path)) { 72 | return $this->hasMethod; 73 | } 74 | 75 | if ( ! $this->hasMethod) { 76 | return false; 77 | } 78 | 79 | return $this->getFromMethod($path); 80 | } 81 | 82 | /** 83 | * Get the URL using a `getUrl()` method on the adapter. 84 | * 85 | * @param string $path 86 | * @return string 87 | */ 88 | protected function getFromMethod($path) 89 | { 90 | return $this->adapter->getUrl($path); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/Utility/HasDir.php: -------------------------------------------------------------------------------- 1 | adapter = $filesystem->getAdapter(); 36 | // For a cached adapter, get the underlying instance 37 | if ($this->adapter instanceof CachedAdapter) { 38 | $this->adapter = $this->adapter->getAdapter(); 39 | } 40 | //TODO: Check on actual implementations, not just an existing method 41 | $this->hasMethod = method_exists($this->adapter, 'hasDir'); 42 | } 43 | } 44 | /** 45 | * Get the method name. 46 | * 47 | * @return string 48 | */ 49 | public function getMethod() 50 | { 51 | return 'hasDir'; 52 | } 53 | /** 54 | * Get the public url 55 | * 56 | * @param string $path path to file 57 | * 58 | * @return string|false 59 | */ 60 | public function handle($path = null) 61 | { 62 | if (is_null($path)) { 63 | return $this->hasMethod; 64 | } 65 | if ( ! $this->hasMethod) { 66 | return false; 67 | } 68 | return $this->getFromMethod($path); 69 | } 70 | /** 71 | * Get the URL using a `hasDir()` method on the adapter. 72 | * 73 | * @param string $path 74 | * @return string 75 | */ 76 | protected function getFromMethod($path) 77 | { 78 | $res = $this->adapter->hasDir($path); 79 | if (is_array($res)) { 80 | return isset($res['hasdir'])? $res['hasdir'] : true; 81 | } else { 82 | return $res; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/mime.types: -------------------------------------------------------------------------------- 1 | # This file controls what Internet media types are sent to the client for 2 | # given file extension(s). Sending the correct media type to the client 3 | # is important so they know how to handle the content of the file. 4 | # For more information about Internet media types, please read 5 | # RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type 6 | # registry is at . 7 | 8 | # MIME type Extension 9 | application/andrew-inset ez 10 | application/chemtool cht 11 | application/dicom dcm 12 | application/docbook+xml docbook 13 | application/ecmascript ecma 14 | application/flash-video flv 15 | application/illustrator ai 16 | application/javascript js 17 | application/mac-binhex40 18 | application/mathematica nb 19 | application/msword doc 20 | application/octet-stream bin 21 | application/oda oda 22 | application/ogg ogg 23 | application/pdf pdf 24 | application/pgp pgp 25 | application/pgp-encrypted 26 | application/pgp-encrypted pgp gpg 27 | application/pgp-keys 28 | application/pgp-keys skr pkr 29 | application/pgp-signature 30 | application/pgp-signature sig 31 | application/pkcs7-mime 32 | application/pkcs7-signature p7s 33 | application/postscript ps 34 | application/rtf rtf 35 | application/sdp sdp 36 | application/smil smil smi sml 37 | application/stuffit sit 38 | application/vnd.corel-draw cdr 39 | application/vnd.hp-hpgl hpgl 40 | application/vnd.hp-pcl pcl 41 | application/vnd.lotus-1-2-3 123 wk1 wk3 wk4 wks 42 | application/vnd.mozilla.xul+xml xul 43 | application/vnd.ms-excel xls xlc xll xlm xlw xla xlt xld 44 | application/vnd.ms-powerpoint ppz ppt pps pot 45 | application/vnd.oasis.opendocument.chart odc 46 | application/vnd.oasis.opendocument.database odb 47 | application/vnd.oasis.opendocument.formula odf 48 | application/vnd.oasis.opendocument.graphics odg 49 | application/vnd.oasis.opendocument.graphics-template otg 50 | application/vnd.oasis.opendocument.image odi 51 | application/vnd.oasis.opendocument.presentation odp 52 | application/vnd.oasis.opendocument.presentation-template otp 53 | application/vnd.oasis.opendocument.spreadsheet ods 54 | application/vnd.oasis.opendocument.spreadsheet-template ots 55 | application/vnd.oasis.opendocument.text odt 56 | application/vnd.oasis.opendocument.text-master odm 57 | application/vnd.oasis.opendocument.text-template ott 58 | application/vnd.oasis.opendocument.text-web oth 59 | application/vnd.palm pdb 60 | application/vnd.rn-realmedia 61 | application/vnd.rn-realmedia rm 62 | application/vnd.rn-realmedia-secure rms 63 | application/vnd.rn-realmedia-vbr rmvb 64 | application/vnd.stardivision.calc sdc 65 | application/vnd.stardivision.chart sds 66 | application/vnd.stardivision.draw sda 67 | application/vnd.stardivision.impress sdd sdp 68 | application/vnd.stardivision.mail smd 69 | application/vnd.stardivision.math smf 70 | application/vnd.stardivision.writer sdw vor sgl 71 | application/vnd.sun.xml.calc sxc 72 | application/vnd.sun.xml.calc.template stc 73 | application/vnd.sun.xml.draw sxd 74 | application/vnd.sun.xml.draw.template std 75 | application/vnd.sun.xml.impress sxi 76 | application/vnd.sun.xml.impress.template sti 77 | application/vnd.sun.xml.math sxm 78 | application/vnd.sun.xml.writer sxw 79 | application/vnd.sun.xml.writer.global sxg 80 | application/vnd.sun.xml.writer.template stw 81 | application/vnd.wordperfect wpd 82 | application/x-abiword abw abw.CRASHED abw.gz zabw 83 | application/x-amipro sam 84 | application/x-anjuta-project prj 85 | application/x-applix-spreadsheet as 86 | application/x-applix-word aw 87 | application/x-arc 88 | application/x-archive a 89 | application/x-arj arj 90 | application/x-asax asax 91 | application/x-ascx ascx 92 | application/x-ashx ashx 93 | application/x-asix asix 94 | application/x-asmx asmx 95 | application/x-asp asp 96 | application/x-awk 97 | application/x-axd axd 98 | application/x-bcpio bcpio 99 | application/x-bittorrent torrent 100 | application/x-blender blender blend BLEND 101 | application/x-bzip bz bz2 102 | application/x-bzip bz2 bz 103 | application/x-bzip-compressed-tar tar.bz tar.bz2 104 | application/x-bzip-compressed-tar tar.bz tar.bz2 tbz tbz2 105 | application/x-cd-image iso 106 | application/x-cgi cgi 107 | application/x-chess-pgn pgn 108 | application/x-chm chm 109 | application/x-class-file 110 | application/x-cmbx cmbx 111 | application/x-compress Z 112 | application/x-compressed-tar tar.gz tar.Z tgz taz 113 | application/x-compressed-tar tar.gz tgz 114 | application/x-config config 115 | application/x-core 116 | application/x-cpio cpio 117 | application/x-cpio-compressed cpio.gz 118 | application/x-csh csh 119 | application/x-cue cue 120 | application/x-dbase dbf 121 | application/x-dbm 122 | application/x-dc-rom dc 123 | application/x-deb deb 124 | application/x-designer ui 125 | application/x-desktop desktop kdelnk 126 | application/x-devhelp devhelp 127 | application/x-dia-diagram dia 128 | application/x-disco disco 129 | application/x-dvi dvi 130 | application/x-e-theme etheme 131 | application/x-egon egon 132 | application/x-executable exe 133 | application/x-font-afm afm 134 | application/x-font-bdf bdf 135 | application/x-font-dos 136 | application/x-font-framemaker 137 | application/x-font-libgrx 138 | application/x-font-linux-psf psf 139 | application/x-font-otf 140 | application/x-font-pcf pcf 141 | application/x-font-pcf pcf.gz 142 | application/x-font-speedo spd 143 | application/x-font-sunos-news 144 | application/x-font-tex 145 | application/x-font-tex-tfm 146 | application/x-font-ttf ttc TTC 147 | application/x-font-ttf ttf 148 | application/x-font-type1 pfa pfb gsf pcf.Z 149 | application/x-font-vfont 150 | application/x-frame 151 | application/x-frontline aop 152 | application/x-gameboy-rom gb 153 | application/x-gdbm 154 | application/x-gdesklets-display display 155 | application/x-genesis-rom gen md 156 | application/x-gettext-translation gmo 157 | application/x-glabels glabels 158 | application/x-glade glade 159 | application/x-gmc-link 160 | application/x-gnome-db-connection connection 161 | application/x-gnome-db-database database 162 | application/x-gnome-stones caves 163 | application/x-gnucash gnucash gnc xac 164 | application/x-gnumeric gnumeric 165 | application/x-graphite gra 166 | application/x-gtar gtar 167 | application/x-gtktalog 168 | application/x-gzip gz 169 | application/x-gzpostscript ps.gz 170 | application/x-hdf hdf 171 | application/x-ica ica 172 | application/x-ipod-firmware 173 | application/x-jamin jam 174 | application/x-jar jar 175 | application/x-java class 176 | application/x-java-archive jar ear war 177 | 178 | application/x-jbuilder-project jpr jpx 179 | application/x-karbon karbon 180 | application/x-kchart chrt 181 | application/x-kformula kfo 182 | application/x-killustrator kil 183 | application/x-kivio flw 184 | application/x-kontour kon 185 | application/x-kpovmodeler kpm 186 | application/x-kpresenter kpr kpt 187 | application/x-krita kra 188 | application/x-kspread ksp 189 | application/x-kspread-crypt 190 | application/x-ksysv-package 191 | application/x-kugar kud 192 | application/x-kword kwd kwt 193 | application/x-kword-crypt 194 | application/x-lha lha lzh 195 | application/x-lha lzh 196 | application/x-lhz lhz 197 | application/x-linguist ts 198 | application/x-lyx lyx 199 | application/x-lzop lzo 200 | application/x-lzop-compressed-tar tar.lzo tzo 201 | application/x-macbinary 202 | application/x-machine-config 203 | application/x-magicpoint mgp 204 | application/x-master-page master 205 | application/x-matroska mkv 206 | application/x-mdp mdp 207 | application/x-mds mds 208 | application/x-mdsx mdsx 209 | application/x-mergeant mergeant 210 | application/x-mif mif 211 | application/x-mozilla-bookmarks 212 | application/x-mps mps 213 | application/x-ms-dos-executable exe 214 | application/x-mswinurl 215 | application/x-mswrite wri 216 | application/x-msx-rom msx 217 | application/x-n64-rom n64 218 | application/x-nautilus-link 219 | application/x-nes-rom nes 220 | application/x-netcdf cdf nc 221 | application/x-netscape-bookmarks 222 | application/x-object o 223 | application/x-ole-storage 224 | application/x-oleo oleo 225 | application/x-palm-database 226 | application/x-palm-database pdb prc 227 | application/x-par2 PAR2 par2 228 | application/x-pef-executable 229 | application/x-perl pl pm al perl 230 | application/x-php php php3 php4 231 | application/x-pkcs12 p12 pfx 232 | application/x-planner planner mrproject 233 | application/x-planperfect pln 234 | application/x-prjx prjx 235 | application/x-profile 236 | application/x-ptoptimizer-script pto 237 | application/x-pw pw 238 | application/x-python-bytecode pyc pyo 239 | application/x-quattro-pro wb1 wb2 wb3 240 | application/x-quattropro wb1 wb2 wb3 241 | application/x-qw qif 242 | application/x-rar rar 243 | application/x-rar-compressed rar 244 | application/x-rdp rdp 245 | application/x-reject rej 246 | application/x-remoting rem 247 | application/x-resources resources 248 | application/x-resourcesx resx 249 | application/x-rpm rpm 250 | application/x-ruby 251 | application/x-sc 252 | application/x-sc sc 253 | application/x-scribus sla sla.gz scd scd.gz 254 | application/x-shar shar 255 | application/x-shared-library-la la 256 | application/x-sharedlib so 257 | application/x-shellscript sh 258 | application/x-shockwave-flash swf 259 | application/x-siag siag 260 | application/x-slp 261 | application/x-smil kino 262 | application/x-smil smi smil 263 | application/x-sms-rom sms gg 264 | application/x-soap-remoting soap 265 | application/x-streamingmedia ssm 266 | application/x-stuffit 267 | application/x-stuffit bin sit 268 | application/x-sv4cpio sv4cpio 269 | application/x-sv4crc sv4crc 270 | application/x-tar tar 271 | application/x-tarz tar.Z 272 | application/x-tex-gf gf 273 | application/x-tex-pk k 274 | application/x-tgif obj 275 | application/x-theme theme 276 | application/x-toc toc 277 | application/x-toutdoux 278 | application/x-trash bak old sik 279 | application/x-troff tr roff t 280 | application/x-troff-man man 281 | application/x-troff-man-compressed 282 | application/x-tzo tar.lzo tzo 283 | application/x-ustar ustar 284 | application/x-wais-source src 285 | application/x-web-config 286 | application/x-wpg wpg 287 | application/x-wsdl wsdl 288 | application/x-x509-ca-cert der cer crt cert pem 289 | application/x-xbel xbel 290 | application/x-zerosize 291 | application/x-zoo zoo 292 | application/xhtml+xml xhtml 293 | application/zip zip 294 | audio/ac3 ac3 295 | audio/basic au snd 296 | audio/midi mid midi 297 | audio/mpeg mp3 298 | audio/prs.sid sid psid 299 | audio/vnd.rn-realaudio ra 300 | audio/x-aac aac 301 | audio/x-adpcm 302 | audio/x-aifc 303 | audio/x-aiff aif aiff 304 | audio/x-aiff aiff aif aifc 305 | audio/x-aiffc 306 | audio/x-flac flac 307 | audio/x-m4a m4a 308 | audio/x-mod mod ult uni XM m15 mtm 669 309 | audio/x-mp3-playlist 310 | audio/x-mpeg 311 | audio/x-mpegurl m3u 312 | audio/x-ms-asx 313 | audio/x-pn-realaudio ra ram rm 314 | audio/x-pn-realaudio ram rmm 315 | audio/x-riff 316 | audio/x-s3m s3m 317 | audio/x-scpls pls 318 | audio/x-scpls pls xpl 319 | audio/x-stm stm 320 | audio/x-voc voc 321 | audio/x-wav wav 322 | audio/x-xi xi 323 | audio/x-xm xm 324 | image/bmp bmp 325 | image/cgm cgm 326 | image/dpx 327 | image/fax-g3 g3 328 | image/g3fax 329 | image/gif gif 330 | image/ief ief 331 | image/jpeg jpeg jpg jpe 332 | image/jpeg2000 jp2 333 | image/png png 334 | image/rle rle 335 | image/svg+xml svg 336 | image/tiff tif tiff 337 | image/vnd.djvu djvu djv 338 | image/vnd.dwg dwg 339 | image/vnd.dxf dxf 340 | image/x-3ds 3ds 341 | image/x-applix-graphics ag 342 | image/x-cmu-raster ras 343 | image/x-compressed-xcf xcf.gz xcf.bz2 344 | image/x-dcraw bay BAY bmq BMQ cr2 CR2 crw CRW cs1 CS1 dc2 DC2 dcr DCR fff FFF k25 K25 kdc KDC mos MOS mrw MRW nef NEF orf ORF pef PEF raf RAF rdc RDC srf SRF x3f X3F 345 | image/x-dib 346 | image/x-eps eps epsi epsf 347 | image/x-fits fits 348 | image/x-fpx 349 | image/x-icb icb 350 | image/x-ico ico 351 | image/x-iff iff 352 | image/x-ilbm ilbm 353 | image/x-jng jng 354 | image/x-lwo lwo lwob 355 | image/x-lws lws 356 | image/x-msod msod 357 | image/x-niff 358 | image/x-pcx 359 | image/x-photo-cd pcd 360 | image/x-pict pict pict1 pict2 361 | image/x-portable-anymap pnm 362 | image/x-portable-bitmap pbm 363 | image/x-portable-graymap pgm 364 | image/x-portable-pixmap ppm 365 | image/x-psd psd 366 | image/x-rgb rgb 367 | image/x-sgi sgi 368 | image/x-sun-raster sun 369 | image/x-tga tga 370 | image/x-win-bitmap cur 371 | image/x-wmf wmf 372 | image/x-xbitmap xbm 373 | image/x-xcf xcf 374 | image/x-xfig fig 375 | image/x-xpixmap xpm 376 | image/x-xwindowdump xwd 377 | inode/blockdevice 378 | inode/chardevice 379 | inode/directory 380 | inode/fifo 381 | inode/mount-point 382 | inode/socket 383 | inode/symlink 384 | message/delivery-status 385 | message/disposition-notification 386 | message/external-body 387 | message/news 388 | message/partial 389 | message/rfc822 390 | message/x-gnu-rmail 391 | model/vrml wrl 392 | multipart/alternative 393 | multipart/appledouble 394 | multipart/digest 395 | multipart/encrypted 396 | multipart/mixed 397 | multipart/related 398 | multipart/report 399 | multipart/signed 400 | multipart/x-mixed-replace 401 | text/calendar vcs ics 402 | text/css css CSSL 403 | text/directory vcf vct gcrd 404 | text/enriched 405 | text/html html htm 406 | text/htmlh 407 | text/mathml mml 408 | text/plain txt asc 409 | text/rdf rdf 410 | text/rfc822-headers 411 | text/richtext rtx 412 | text/rss rss 413 | text/sgml sgml sgm 414 | text/spreadsheet sylk slk 415 | text/tab-separated-values tsv 416 | text/vnd.rn-realtext rt 417 | text/vnd.wap.wml wml 418 | text/x-adasrc adb ads 419 | text/x-authors 420 | text/x-bibtex bib 421 | text/x-boo boo 422 | text/x-c++hdr hh 423 | text/x-c++src cpp cxx cc C c++ 424 | text/x-chdr h h++ hp 425 | text/x-comma-separated-values csv 426 | text/x-copying 427 | text/x-credits 428 | text/x-csrc c 429 | text/x-dcl dcl 430 | text/x-dsl dsl 431 | text/x-dsrc d 432 | text/x-dtd dtd 433 | text/x-emacs-lisp el 434 | text/x-fortran f 435 | text/x-gettext-translation po 436 | text/x-gettext-translation-template pot 437 | text/x-gtkrc 438 | text/x-haskell hs 439 | text/x-idl idl 440 | text/x-install 441 | text/x-java java 442 | text/x-js js 443 | text/x-ksysv-log 444 | text/x-literate-haskell lhs 445 | text/x-log log 446 | text/x-makefile 447 | text/x-moc moc 448 | text/x-msil il 449 | text/x-nemerle n 450 | text/x-objcsrc m 451 | text/x-pascal p pas 452 | text/x-patch diff patch 453 | text/x-python py 454 | text/x-readme 455 | text/x-rng rng 456 | text/x-scheme scm 457 | text/x-setext etx 458 | text/x-speech 459 | text/x-sql sql 460 | text/x-suse-ymp ymp 461 | text/x-suse-ymu ymu 462 | text/x-tcl tcl tk 463 | text/x-tex tex ltx sty cls 464 | text/x-texinfo texi texinfo 465 | text/x-texmacs tm ts 466 | text/x-troff-me me 467 | text/x-troff-mm mm 468 | text/x-troff-ms ms 469 | text/x-uil uil 470 | text/x-uri uri url 471 | text/x-vb vb 472 | text/x-xds xds 473 | text/x-xmi xmi 474 | text/x-xsl xsl 475 | text/x-xslfo fo xslfo 476 | text/x-xslt xslt xsl 477 | text/xmcd 478 | text/xml xml 479 | video/3gpp 3gp 480 | video/dv dv dif 481 | video/isivideo 482 | video/mpeg mpeg mpg mp2 mpe vob dat 483 | video/quicktime qt mov moov qtvr 484 | video/vivo 485 | video/vnd.rn-realvideo rv 486 | video/wavelet 487 | video/x-3gpp2 3g2 488 | video/x-anim anim[1-9j] 489 | video/x-avi 490 | video/x-flic fli flc 491 | video/x-mng mng 492 | video/x-ms-asf asf asx 493 | video/x-ms-wmv wmv 494 | video/x-msvideo avi 495 | video/x-nsv nsv NSV 496 | video/x-real-video 497 | video/x-sgi-movie movie 498 | application/x-java-jnlp-file jnlp 499 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx 500 | application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx 501 | application/vnd.ms-word.document.macroEnabled.12 docm 502 | application/vnd.ms-word.template.macroEnabled.12 dotm 503 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx 504 | application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx 505 | application/vnd.ms-excel.sheet.macroEnabled.12 xlsm 506 | application/vnd.ms-excel.template.macroEnabled.12 xltm 507 | application/vnd.ms-excel.addin.macroEnabled.12 xlam 508 | application/vnd.ms-excel.sheet.binary.macroEnabled.12 xlsb 509 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx 510 | application/vnd.openxmlformats-officedocument.presentationml.template potx 511 | application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx 512 | application/vnd.ms-powerpoint.addin.macroEnabled.12 ppam 513 | --------------------------------------------------------------------------------