PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/administrator/components/com_k2/lib/elfinder/elFinder.class.php

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