├── 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 | *
1169 | * - $item['path'] - absolute remote FTP path
1170 | * - $item['type'] - either 'f' for file or 'd' for directory
1171 | *
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 |
--------------------------------------------------------------------------------