PageRenderTime 58ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/protected/extensions/elfinder/php/elFinder.class.php

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