PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/htdocs/includes/lib/elfinder/php/elFinder.class.php

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