OSDN Git Service

初回コミット(v2.6.17.1)
[magic3/magic3.git] / scripts / elfinder-2.0-rc1 / php / elFinder.class.php
1 <?php
2
3 /**
4  * elFinder - file manager for web.
5  * Core class.
6  *
7  * @package elfinder
8  * @author Dmitry (dio) Levashov
9  * @author Troex Nevelin
10  * @author Alexey Sukhotin
11  **/
12 class elFinder {
13         
14         /**
15          * API version number
16          *
17          * @var string
18          **/
19         protected $version = '2.0';
20         
21         /**
22          * Storages (root dirs)
23          *
24          * @var array
25          **/
26         protected $volumes = array();
27         
28         /**
29          * Mounted volumes count
30          * Required to create unique volume id
31          *
32          * @var int
33          **/
34         public static $volumesCnt = 1;
35         
36         /**
37          * Default root (storage)
38          *
39          * @var elFinderStorageDriver
40          **/
41         protected $default = null;
42         
43         /**
44          * Commands and required arguments list
45          *
46          * @var array
47          **/
48         protected $commands = array(
49                 'open'      => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false),
50                 'ls'        => array('target' => true, 'mimes' => false),
51                 'tree'      => array('target' => true),
52                 'parents'   => array('target' => true),
53                 'tmb'       => array('targets' => true),
54                 'file'      => array('target' => true, 'download' => false),
55                 'size'      => array('targets' => true),
56                 'mkdir'     => array('target' => true, 'name' => true),
57                 'mkfile'    => array('target' => true, 'name' => true, 'mimes' => false),
58                 'rm'        => array('targets' => true),
59                 'rename'    => array('target' => true, 'name' => true, 'mimes' => false),
60                 'duplicate' => array('targets' => true, 'suffix' => false),
61                 'paste'     => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false),
62                 'upload'    => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false),
63                 'get'       => array('target' => true),
64                 'put'       => array('target' => true, 'content' => '', 'mimes' => false),
65                 'archive'   => array('targets' => true, 'type' => true, 'mimes' => false),
66                 'extract'   => array('target' => true, 'mimes' => false),
67                 'search'    => array('q' => true, 'mimes' => false),
68                 'info'      => array('targets' => true),
69                 'dim'       => array('target' => true),
70                 'resize'    => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false)
71         );
72         
73         /**
74          * Commands listeners
75          *
76          * @var array
77          **/
78         protected $listeners = array();
79         
80         /**
81          * script work time for debug
82          *
83          * @var string
84          **/
85         protected $time = 0;
86         /**
87          * Is elFinder init correctly?
88          *
89          * @var bool
90          **/
91         protected $loaded = false;
92         /**
93          * Send debug to client?
94          *
95          * @var string
96          **/
97         protected $debug = false;
98         
99         /**
100          * undocumented class variable
101          *
102          * @var string
103          **/
104         protected $uploadDebug = '';
105         
106         /**
107          * Errors from not mounted volumes
108          *
109          * @var array
110          **/
111         public $mountErrors = array();
112         
113         // Errors messages
114         const ERROR_UNKNOWN           = 'errUnknown';
115         const ERROR_UNKNOWN_CMD       = 'errUnknownCmd';
116         const ERROR_CONF              = 'errConf';
117         const ERROR_CONF_NO_JSON      = 'errJSON';
118         const ERROR_CONF_NO_VOL       = 'errNoVolumes';
119         const ERROR_INV_PARAMS        = 'errCmdParams';
120         const ERROR_OPEN              = 'errOpen';
121         const ERROR_DIR_NOT_FOUND     = 'errFolderNotFound';
122         const ERROR_FILE_NOT_FOUND    = 'errFileNotFound';     // 'File not found.'
123         const ERROR_TRGDIR_NOT_FOUND  = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.'
124         const ERROR_NOT_DIR           = 'errNotFolder';
125         const ERROR_NOT_FILE          = 'errNotFile';
126         const ERROR_PERM_DENIED       = 'errPerm';
127         const ERROR_LOCKED            = 'errLocked';        // '"$1" is locked and can not be renamed, moved or removed.'
128         const ERROR_EXISTS            = 'errExists';        // 'File named "$1" already exists.'
129         const ERROR_INVALID_NAME      = 'errInvName';       // 'Invalid file name.'
130         const ERROR_MKDIR             = 'errMkdir';
131         const ERROR_MKFILE            = 'errMkfile';
132         const ERROR_RENAME            = 'errRename';
133         const ERROR_COPY              = 'errCopy';
134         const ERROR_MOVE              = 'errMove';
135         const ERROR_COPY_FROM         = 'errCopyFrom';
136         const ERROR_COPY_TO           = 'errCopyTo';
137         const ERROR_COPY_ITSELF       = 'errCopyInItself';
138         const ERROR_REPLACE           = 'errReplace';          // 'Unable to replace "$1".'
139         const ERROR_RM                = 'errRm';               // 'Unable to remove "$1".'
140         const ERROR_RM_SRC            = 'errRmSrc';            // 'Unable remove source file(s)'
141         const ERROR_UPLOAD            = 'errUpload';           // 'Upload error.'
142         const ERROR_UPLOAD_FILE       = 'errUploadFile';       // 'Unable to upload "$1".'
143         const ERROR_UPLOAD_NO_FILES   = 'errUploadNoFiles';    // 'No files found for upload.'
144         const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize';  // 'Data exceeds the maximum allowed size.'
145         const ERROR_UPLOAD_FILE_SIZE  = 'errUploadFileSize';   // 'File exceeds maximum allowed size.'
146         const ERROR_UPLOAD_FILE_MIME  = 'errUploadMime';       // 'File type not allowed.'
147         const ERROR_UPLOAD_TRANSFER   = 'errUploadTransfer';   // '"$1" transfer error.'
148         // const ERROR_ACCESS_DENIED     = 'errAccess';
149         const ERROR_NOT_REPLACE       = 'errNotReplace';       // Object "$1" already exists at this location and can not be replaced with object of another type.
150         const ERROR_SAVE              = 'errSave';
151         const ERROR_EXTRACT           = 'errExtract';
152         const ERROR_ARCHIVE           = 'errArchive';
153         const ERROR_NOT_ARCHIVE       = 'errNoArchive';
154         const ERROR_ARCHIVE_TYPE      = 'errArcType';
155         const ERROR_ARC_SYMLINKS      = 'errArcSymlinks';
156         const ERROR_ARC_MAXSIZE       = 'errArcMaxSize';
157         const ERROR_RESIZE            = 'errResize';
158         const ERROR_UNSUPPORT_TYPE    = 'errUsupportType';
159         const ERROR_NOT_UTF8_CONTENT  = 'errNotUTF8Content';
160         
161         /**
162          * Constructor
163          *
164          * @param  array  elFinder and roots configurations
165          * @return void
166          * @author Dmitry (dio) Levashov
167          **/
168         public function __construct($opts) {
169                 
170                 $this->time  = $this->utime();
171                 $this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false);
172                 
173                 setlocale(LC_ALL, !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8');
174
175                 // bind events listeners
176                 if (!empty($opts['bind']) && is_array($opts['bind'])) {
177                         foreach ($opts['bind'] as $cmd => $handler) {
178                                 $this->bind($cmd, $handler);
179                         }
180                 }
181
182                 // "mount" volumes
183                 if (isset($opts['roots']) && is_array($opts['roots'])) {
184                         
185                         foreach ($opts['roots'] as $i => $o) {
186                                 $class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : '');
187
188                                 if (class_exists($class)) {
189                                         $volume = new $class();
190
191                                         if ($volume->mount($o)) {
192                                                 // unique volume id (ends on "_") - used as prefix to files hash
193                                                 $id = $volume->id();
194                                                 
195                                                 $this->volumes[$id] = $volume;
196                                                 if (!$this->default && $volume->isReadable()) {
197                                                         $this->default = $this->volumes[$id]; 
198                                                 }
199                                         } else {
200                                                 $this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error());
201                                         }
202                                 } else {
203                                         $this->mountErrors[] = 'Driver "'.$class.'" does not exists';
204                                 }
205                         }
206                 }
207                 // if at least one redable volume - ii desu >_<
208                 $this->loaded = !empty($this->default);
209         }
210         
211         /**
212          * Return true if fm init correctly
213          *
214          * @return bool
215          * @author Dmitry (dio) Levashov
216          **/
217         public function loaded() {
218                 return $this->loaded;
219         }
220         
221         /**
222          * Return version (api) number
223          *
224          * @return string
225          * @author Dmitry (dio) Levashov
226          **/
227         public function version() {
228                 return $this->version;
229         }
230         
231         /**
232          * Add handler to elFinder command
233          *
234          * @param  string  command name
235          * @param  string|array  callback name or array(object, method)
236          * @return elFinder
237          * @author Dmitry (dio) Levashov
238          **/
239         public function bind($cmd, $handler) {
240                 $cmds = array_map('trim', explode(' ', $cmd));
241                 
242                 foreach ($cmds as $cmd) {
243                         if ($cmd) {
244                                 if (!isset($this->listeners[$cmd])) {
245                                         $this->listeners[$cmd] = array();
246                                 }
247
248                                 if ((is_array($handler) && count($handler) == 2 && is_object($handler[0]) && method_exists($handler[0], $handler[1]))
249                                 || function_exists($handler)) {
250                                         $this->listeners[$cmd][] = $handler;
251                                 }
252                         }
253                 }
254
255                 return $this;
256         }
257         
258         /**
259          * Remove event (command exec) handler
260          *
261          * @param  string  command name
262          * @param  string|array  callback name or array(object, method)
263          * @return elFinder
264          * @author Dmitry (dio) Levashov
265          **/
266         public function unbind($cmd, $handler) {
267                 if (!empty($this->listeners[$cmd])) {
268                         foreach ($this->listeners[$cmd] as $i => $h) {
269                                 if ($h === $handler) {
270                                         unset($this->listeners[$cmd][$i]);
271                                         return $this;
272                                 }
273                         }
274                 }
275                 return $this;
276         }
277         
278         /**
279          * Return true if command exists
280          *
281          * @param  string  command name
282          * @return bool
283          * @author Dmitry (dio) Levashov
284          **/
285         public function commandExists($cmd) {
286                 return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
287         }
288         
289         /**
290          * Return command required arguments info
291          *
292          * @param  string  command name
293          * @return array
294          * @author Dmitry (dio) Levashov
295          **/
296         public function commandArgsList($cmd) {
297                 return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
298         }
299         
300         /**
301          * Exec command and return result
302          *
303          * @param  string  $cmd  command name
304          * @param  array   $args command arguments
305          * @return array
306          * @author Dmitry (dio) Levashov
307          **/
308         public function exec($cmd, $args) {
309                 
310                 if (!$this->loaded) {
311                         return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
312                 }
313                 
314                 if (!$this->commandExists($cmd)) {
315                         return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
316                 }
317                 
318                 if (!empty($args['mimes']) && is_array($args['mimes'])) {
319                         foreach ($this->volumes as $id => $v) {
320                                 $this->volumes[$id]->setMimesFilter($args['mimes']);
321                         }
322                 }
323                 
324                 $result = $this->$cmd($args);
325                 
326                 if (isset($result['removed'])) {
327                         foreach ($this->volumes as $volume) {
328                                 $result['removed'] = array_merge($result['removed'], $volume->removed());
329                                 $volume->resetRemoved();
330                         }
331                 }
332                 
333                 // call handlers for this command
334                 if (!empty($this->listeners[$cmd])) {
335                         foreach ($this->listeners[$cmd] as $handler) {
336                                 if ((is_array($handler) && $handler[0]->{$handler[1]}($cmd, $result, $args, $this))
337                                 ||  (!is_array($handler) && $handler($cmd, $result, $args, $this))) {
338                                         // handler return true to force sync client after command completed
339                                         $result['sync'] = true;
340                                 }
341                         }
342                 }
343                 
344                 // replace removed files info with removed files hashes
345                 if (!empty($result['removed'])) {
346                         $removed = array();
347                         foreach ($result['removed'] as $file) {
348                                 $removed[] = $file['hash'];
349                         }
350                         $result['removed'] = array_unique($removed);
351                 }
352                 // remove hidden files and filter files by mimetypes
353                 if (!empty($result['added'])) {
354                         $result['added'] = $this->filter($result['added']);
355                 }
356                 // remove hidden files and filter files by mimetypes
357                 if (!empty($result['changed'])) {
358                         $result['changed'] = $this->filter($result['changed']);
359                 }
360                 
361                 if ($this->debug || !empty($args['debug'])) {
362                         $result['debug'] = array(
363                                 'connector' => 'php', 
364                                 'phpver'    => PHP_VERSION,
365                                 'time'      => $this->utime() - $this->time,
366                                 'memory'    => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'),
367                                 'upload'    => $this->uploadDebug,
368                                 'volumes'   => array(),
369                                 'mountErrors' => $this->mountErrors
370                                 );
371                         
372                         foreach ($this->volumes as $id => $volume) {
373                                 $result['debug']['volumes'][] = $volume->debug();
374                         }
375                 }
376                 
377                 foreach ($this->volumes as $volume) {
378                         $volume->umount();
379                 }
380                 
381                 return $result;
382         }
383         
384         /**
385          * Return file real path
386          *
387          * @param  string  $hash  file hash
388          * @return string
389          * @author Dmitry (dio) Levashov
390          **/
391         public function realpath($hash) {
392                 if (($volume = $this->volume($hash)) == false) {
393                         return false;
394                 }
395                 return $volume->realpath($hash);
396         }
397         
398         /***************************************************************************/
399         /*                                 commands                                */
400         /***************************************************************************/
401         
402         /**
403          * Normalize error messages
404          *
405          * @return array
406          * @author Dmitry (dio) Levashov
407          **/
408         public function error() {
409                 $errors = array();
410
411                 foreach (func_get_args() as $msg) {
412                         if (is_array($msg)) {
413                                 $errors = array_merge($errors, $msg);
414                         } else {
415                                 $errors[] = $msg;
416                         }
417                 }
418                 
419                 return count($errors) ? $errors : array(self::ERROR_UNKNOWN);
420         }
421         
422         /**
423          * "Open" directory
424          * Return array with following elements
425          *  - cwd          - opened dir info
426          *  - files        - opened dir content [and dirs tree if $args[tree]]
427          *  - api          - api version (if $args[init])
428          *  - uplMaxSize   - if $args[init]
429          *  - error        - on failed
430          *
431          * @param  array  command arguments
432          * @return array
433          * @author Dmitry (dio) Levashov
434          **/
435         protected function open($args) {
436                 $target = $args['target'];
437                 $init   = !empty($args['init']);
438                 $tree   = !empty($args['tree']);
439                 $volume = $this->volume($target);
440                 $cwd    = $volume ? $volume->dir($target, true) : false;
441                 $hash   = $init ? 'default folder' : '#'.$target;
442
443                 // on init request we can get invalid dir hash -
444                 // dir which can not be opened now, but remembered by client,
445                 // so open default dir
446                 if ((!$cwd || !$cwd['read']) && $init) {
447                         $volume = $this->default;
448                         $cwd    = $volume->dir($volume->defaultPath(), true);
449                 }
450                 
451                 if (!$cwd) {
452                         return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
453                 }
454                 if (!$cwd['read']) {
455                         return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
456                 }
457
458                 $files = array();
459
460                 // get folders trees
461                 if ($args['tree']) {
462                         foreach ($this->volumes as $id => $v) {
463
464                                 if (($tree = $v->tree('', 0, $cwd['hash'])) != false) {
465                                         $files = array_merge($files, $tree);
466                                 }
467                         }
468                 }
469
470                 // get current working directory files list and add to $files if not exists in it
471                 if (($ls = $volume->scandir($cwd['hash'])) === false) {
472                         return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
473                 }
474                 
475                 foreach ($ls as $file) {
476                         if (!in_array($file, $files)) {
477                                 $files[] = $file;
478                         }
479                 }
480                 
481                 $result = array(
482                         'cwd'     => $cwd,
483                         'options' => $volume->options($cwd['hash']),
484                         'files'   => $files
485                 );
486
487                 if (!empty($args['init'])) {
488                         $result['api'] = $this->version;
489                         $result['uplMaxSize'] = ini_get('upload_max_filesize');
490                 }
491                 
492                 return $result;
493         }
494         
495         /**
496          * Return dir files names list
497          *
498          * @param  array  command arguments
499          * @return array
500          * @author Dmitry (dio) Levashov
501          **/
502         protected function ls($args) {
503                 $target = $args['target'];
504                 
505                 if (($volume = $this->volume($target)) == false
506                 || ($list = $volume->ls($target)) === false) {
507                         return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
508                 }
509                 return array('list' => $list);
510         }
511         
512         /**
513          * Return subdirs for required directory
514          *
515          * @param  array  command arguments
516          * @return array
517          * @author Dmitry (dio) Levashov
518          **/
519         protected function tree($args) {
520                 $target = $args['target'];
521                 
522                 if (($volume = $this->volume($target)) == false
523                 || ($tree = $volume->tree($target)) == false) {
524                         return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
525                 }
526
527                 return array('tree' => $tree);
528         }
529         
530         /**
531          * Return parents dir for required directory
532          *
533          * @param  array  command arguments
534          * @return array
535          * @author Dmitry (dio) Levashov
536          **/
537         protected function parents($args) {
538                 $target = $args['target'];
539                 
540                 if (($volume = $this->volume($target)) == false
541                 || ($tree = $volume->parents($target)) == false) {
542                         return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
543                 }
544
545                 return array('tree' => $tree);
546         }
547         
548         /**
549          * Return new created thumbnails list
550          *
551          * @param  array  command arguments
552          * @return array
553          * @author Dmitry (dio) Levashov
554          **/
555         protected function tmb($args) {
556                 
557                 $result  = array('images' => array());
558                 $targets = $args['targets'];
559                 
560                 foreach ($targets as $target) {
561                         if (($volume = $this->volume($target)) != false
562                         && (($tmb = $volume->tmb($target)) != false)) {
563                                 $result['images'][$target] = $tmb;
564                         }
565                 }
566                 return $result;
567         }
568         
569         /**
570          * Required to output file in browser when volume URL is not set 
571          * Return array contains opened file pointer, root itself and required headers
572          *
573          * @param  array  command arguments
574          * @return array
575          * @author Dmitry (dio) Levashov
576          **/
577         protected function file($args) {
578                 $target   = $args['target'];
579                 $download = !empty($args['download']);
580                 $h403     = 'HTTP/1.x 403 Access Denied';
581                 $h404     = 'HTTP/1.x 404 Not Found';
582
583                 if (($volume = $this->volume($target)) == false) { 
584                         return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
585                 }
586                 
587                 if (($file = $volume->file($target)) == false) {
588                         return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
589                 }
590                 
591                 if (!$file['read']) {
592                         return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
593                 }
594                 
595                 if (($fp = $volume->open($target)) == false) {
596                         return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
597                 }
598
599                 if ($download) {
600                         $disp = 'attachment';
601                         $mime = 'application/octet-stream';
602                 } else {
603                         $disp  = preg_match('/^(image|text)/i', $file['mime']) || $file['mime'] == 'application/x-shockwave-flash' 
604                                         ? 'inline' 
605                                         : 'attachment';
606                         $mime = $file['mime'];
607                 }
608                 
609                 $filenameEncoded = rawurlencode($file['name']);
610                 if (strpos($filenameEncoded, '%') === false) { // ASCII only
611                         $filename = 'filename="'.$file['name'].'"';
612                 } else {
613                         $ua = $_SERVER["HTTP_USER_AGENT"];
614                         if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
615                                 $filename = 'filename="'.$filenameEncoded.'"';
616                         } else { // RFC 6266 (RFC 2231/RFC 5987)
617                                 $filename = 'filename*=UTF-8\'\''.$filenameEncoded;
618                         }
619                 }
620                 
621                 $result = array(
622                         'volume'  => $volume,
623                         'pointer' => $fp,
624                         'info'    => $file,
625                         'header'  => array(
626                                 'Content-Type: '.$mime, 
627                                 'Content-Disposition: '.$disp.'; '.$filename,
628                                 'Content-Location: '.$file['name'],
629                                 'Content-Transfer-Encoding: binary',
630                                 'Content-Length: '.$file['size'],
631                                 'Connection: close'
632                         )
633                 );
634                 return $result;
635         }
636         
637         /**
638          * Count total files size
639          *
640          * @param  array  command arguments
641          * @return array
642          * @author Dmitry (dio) Levashov
643          **/
644         protected function size($args) {
645                 $size = 0;
646                 
647                 foreach ($args['targets'] as $target) {
648                         if (($volume = $this->volume($target)) == false
649                         || ($file = $volume->file($target)) == false
650                         || !$file['read']) {
651                                 return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
652                         }
653                         
654                         $size += $volume->size($target);
655                 }
656                 return array('size' => $size);
657         }
658         
659         /**
660          * Create directory
661          *
662          * @param  array  command arguments
663          * @return array
664          * @author Dmitry (dio) Levashov
665          **/
666         protected function mkdir($args) {
667                 $target = $args['target'];
668                 $name   = $args['name'];
669                 
670                 if (($volume = $this->volume($target)) == false) {
671                         return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
672                 }
673
674                 return ($dir = $volume->mkdir($target, $name)) == false
675                         ? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
676                         : array('added' => array($dir));
677         }
678         
679         /**
680          * Create empty file
681          *
682          * @param  array  command arguments
683          * @return array
684          * @author Dmitry (dio) Levashov
685          **/
686         protected function mkfile($args) {
687                 $target = $args['target'];
688                 $name   = $args['name'];
689                 
690                 if (($volume = $this->volume($target)) == false) {
691                         return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
692                 }
693
694                 return ($file = $volume->mkfile($target, $args['name'])) == false
695                         ? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
696                         : array('added' => array($file));
697         }
698         
699         /**
700          * Rename file
701          *
702          * @param  array  $args
703          * @return array
704          * @author Dmitry (dio) Levashov
705          **/
706         protected function rename($args) {
707                 $target = $args['target'];
708                 $name   = $args['name'];
709                 
710                 if (($volume = $this->volume($target)) == false
711                 ||  ($rm  = $volume->file($target)) == false) {
712                         return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
713                 }
714                 $rm['realpath'] = $volume->realpath($target);
715                 
716                 return ($file = $volume->rename($target, $name)) == false
717                         ? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
718                         : array('added' => array($file), 'removed' => array($rm));
719         }
720         
721         /**
722          * Duplicate file - create copy with "copy %d" suffix
723          *
724          * @param array  $args  command arguments
725          * @return array
726          * @author Dmitry (dio) Levashov
727          **/
728         protected function duplicate($args) {
729                 $targets = is_array($args['targets']) ? $args['targets'] : array();
730                 $result  = array('added' => array());
731                 $suffix  = empty($args['suffix']) ? 'copy' : $args['suffix'];
732                 
733                 foreach ($targets as $target) {
734                         if (($volume = $this->volume($target)) == false
735                         || ($src = $volume->file($target)) == false) {
736                                 $result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
737                                 break;
738                         }
739                         
740                         if (($file = $volume->duplicate($target, $suffix)) == false) {
741                                 $result['warning'] = $this->error($volume->error());
742                                 break;
743                         }
744                         
745                         $result['added'][] = $file;
746                 }
747                 
748                 return $result;
749         }
750                 
751         /**
752          * Remove dirs/files
753          *
754          * @param array  command arguments
755          * @return array
756          * @author Dmitry (dio) Levashov
757          **/
758         protected function rm($args) {
759                 $targets = is_array($args['targets']) ? $args['targets'] : array();
760                 $result  = array('removed' => array());
761                 
762                 foreach ($targets as $target) {
763                         if (($volume = $this->volume($target)) == false) {
764                                 $result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
765                                 return $result;
766                         }
767                         if (!$volume->rm($target)) {
768                                 $result['warning'] = $this->error($volume->error());
769                                 return $result;
770                         }
771                 }
772
773                 return $result;
774         }
775         
776         /**
777          * Save uploaded files
778          *
779          * @param  array
780          * @return array
781          * @author Dmitry (dio) Levashov
782          **/
783         protected function upload($args) {
784                 $target = $args['target'];
785                 $volume = $this->volume($target);
786                 $files  = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
787                 $result = array('added' => array(), 'header' => empty($args['html']) ? false : 'Content-Type: text/html; charset=utf-8');
788                 
789                 if (empty($files)) {
790                         return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES), 'header' => $header);
791                 }
792                 
793                 if (!$volume) {
794                         return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target), 'header' => $header);
795                 }
796                 
797                 foreach ($files['name'] as $i => $name) {
798                         if (($error = $files['error'][$i]) > 0) {                               
799                                 $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER);
800                                 $this->uploadDebug = 'Upload error code: '.$error;
801                                 break;
802                         }
803                         
804                         $tmpname = $files['tmp_name'][$i];
805                         
806                         if (($fp = fopen($tmpname, 'rb')) == false) {
807                                 $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
808                                 $this->uploadDebug = 'Upload error: unable open tmp file';
809                                 break;
810                         }
811                         
812                         if (($file = $volume->upload($fp, $target, $name, $tmpname)) === false) {
813                                 $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
814                                 fclose($fp);
815                                 break;
816                         }
817                         
818                         fclose($fp);
819                         $result['added'][] = $file;
820                 }
821                 
822                 return $result;
823         }
824                 
825         /**
826          * Copy/move files into new destination
827          *
828          * @param  array  command arguments
829          * @return array
830          * @author Dmitry (dio) Levashov
831          **/
832         protected function paste($args) {
833                 $dst     = $args['dst'];
834                 $targets = is_array($args['targets']) ? $args['targets'] : array();
835                 $cut     = !empty($args['cut']);
836                 $error   = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
837                 $result  = array('added' => array(), 'removed' => array());
838                 
839                 if (($dstVolume = $this->volume($dst)) == false) {
840                         return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
841                 }
842                 
843                 foreach ($targets as $target) {
844                         if (($srcVolume = $this->volume($target)) == false) {
845                                 $result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
846                                 break;
847                         }
848                         
849                         if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
850                                 $result['warning'] = $this->error($dstVolume->error());
851                                 break;
852                         }
853                         
854                         $result['added'][] = $file;
855                 }
856                 return $result;
857         }
858         
859         /**
860          * Return file content
861          *
862          * @param  array  $args  command arguments
863          * @return array
864          * @author Dmitry (dio) Levashov
865          **/
866         protected function get($args) {
867                 $target = $args['target'];
868                 $volume = $this->volume($target);
869                 
870                 if (!$volume || ($file = $volume->file($target)) == false) {
871                         return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
872                 }
873                 
874                 if (($content = $volume->getContents($target)) === false) {
875                         return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
876                 }
877                 
878                 $json = json_encode($content);
879
880                 if ($json == 'null' && strlen($json) < strlen($content)) {
881                         return array('error' => $this->error(self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
882                 }
883                 
884                 return array('content' => $content);
885         }
886         
887         /**
888          * Save content into text file
889          *
890          * @return array
891          * @author Dmitry (dio) Levashov
892          **/
893         protected function put($args) {
894                 $target = $args['target'];
895                 
896                 if (($volume = $this->volume($target)) == false
897                 || ($file = $volume->file($target)) == false) {
898                         return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
899                 }
900                 
901                 if (($file = $volume->putContents($target, $args['content'])) == false) {
902                         return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
903                 }
904                 
905                 return array('changed' => array($file));
906         }
907
908         /**
909          * Extract files from archive
910          *
911          * @param  array  $args  command arguments
912          * @return array
913          * @author Dmitry (dio) Levashov, 
914          * @author Alexey Sukhotin
915          **/
916         protected function extract($args) {
917                 $target = $args['target'];
918                 $mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
919                 $error  = array(self::ERROR_EXTRACT, '#'.$target);
920
921                 if (($volume = $this->volume($target)) == false
922                 || ($file = $volume->file($target)) == false) {
923                         return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
924                 }  
925
926                 return ($file = $volume->extract($target))
927                         ? array('added' => array($file))
928                         : array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
929         }
930         
931         /**
932          * Create archive
933          *
934          * @param  array  $args  command arguments
935          * @return array
936          * @author Dmitry (dio) Levashov, 
937          * @author Alexey Sukhotin
938          **/
939         protected function archive($args) {
940                 $type    = $args['type'];
941                 $targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
942         
943                 if (($volume = $this->volume($targets[0])) == false) {
944                         return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
945                 }
946         
947                 return ($file = $volume->archive($targets, $args['type']))
948                         ? array('added' => array($file))
949                         : array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
950         }
951         
952         /**
953          * Search files
954          *
955          * @param  array  $args  command arguments
956          * @return array
957          * @author Dmitry Levashov
958          **/
959         protected function search($args) {
960                 $q      = trim($args['q']);
961                 $mimes  = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
962                 $result = array();
963
964                 foreach ($this->volumes as $volume) {
965                         $result = array_merge($result, $volume->search($q, $mimes));
966                 }
967                 
968                 return array('files' => $result);
969         }
970         
971         /**
972          * Return file info (used by client "places" ui)
973          *
974          * @param  array  $args  command arguments
975          * @return array
976          * @author Dmitry Levashov
977          **/
978         protected function info($args) {
979                 $files = array();
980                 
981                 foreach ($args['targets'] as $hash) {
982                         if (($volume = $this->volume($hash)) != false
983                         && ($info = $volume->file($hash)) != false) {
984                                 $files[] = $info;
985                         }
986                 }
987                 
988                 return array('files' => $files);
989         }
990         
991         /**
992          * Return image dimmensions
993          *
994          * @param  array  $args  command arguments
995          * @return array
996          * @author Dmitry (dio) Levashov
997          **/
998         protected function dim($args) {
999                 $target = $args['target'];
1000                 
1001                 if (($volume = $this->volume($target)) != false) {
1002                         $dim = $volume->dimensions($target);
1003                         return $dim ? array('dim' => $dim) : array();
1004                 }
1005                 return array();
1006         }
1007         
1008         /**
1009          * Resize image
1010          *
1011          * @param  array  command arguments
1012          * @return array
1013          * @author Dmitry (dio) Levashov
1014          * @author Alexey Sukhotin
1015          **/
1016         protected function resize($args) {
1017                 $target = $args['target'];
1018                 $width  = $args['width'];
1019                 $height = $args['height'];
1020                 $x      = (int)$args['x'];
1021                 $y      = (int)$args['y'];
1022                 $mode   = $args['mode'];
1023                 $bg     = null;
1024                 $degree = (int)$args['degree'];
1025                 
1026                 if (($volume = $this->volume($target)) == false
1027                 || ($file = $volume->file($target)) == false) {
1028                         return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
1029                 }
1030
1031                 return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree))
1032                         ? array('changed' => array($file))
1033                         : array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
1034         }
1035         
1036         /***************************************************************************/
1037         /*                                   utils                                 */
1038         /***************************************************************************/
1039         
1040         /**
1041          * Return root - file's owner
1042          *
1043          * @param  string  file hash
1044          * @return elFinderStorageDriver
1045          * @author Dmitry (dio) Levashov
1046          **/
1047         protected function volume($hash) {
1048                 foreach ($this->volumes as $id => $v) {
1049                         if (strpos(''.$hash, $id) === 0) {
1050                                 return $this->volumes[$id];
1051                         } 
1052                 }
1053                 return false;
1054         }
1055         
1056         /**
1057          * Return files info array 
1058          *
1059          * @param  array  $data  one file info or files info
1060          * @return array
1061          * @author Dmitry (dio) Levashov
1062          **/
1063         protected function toArray($data) {
1064                 return isset($data['hash']) || !is_array($data) ? array($data) : $data;
1065         }
1066         
1067         /**
1068          * Return fils hashes list
1069          *
1070          * @param  array  $files  files info
1071          * @return array
1072          * @author Dmitry (dio) Levashov
1073          **/
1074         protected function hashes($files) {
1075                 $ret = array();
1076                 foreach ($files as $file) {
1077                         $ret[] = $file['hash'];
1078                 }
1079                 return $ret;
1080         }
1081         
1082         /**
1083          * Remove from files list hidden files and files with required mime types
1084          *
1085          * @param  array  $files  files info
1086          * @return array
1087          * @author Dmitry (dio) Levashov
1088          **/
1089         protected function filter($files) {
1090                 foreach ($files as $i => $file) {
1091                         if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
1092                                 unset($files[$i]);
1093                         }
1094                 }
1095                 return array_merge($files, array());
1096         }
1097         
1098         protected function utime() {
1099                 $time = explode(" ", microtime());
1100                 return (double)$time[1] + (double)$time[0];
1101         }
1102         
1103 } // END class