PageRenderTime 64ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/htdocs/man/themes/pertho_admin_v1.3/lib/elfinder/php/elFinderVolumeDriver.class.php

https://bitbucket.org/speedealing/speedealing
PHP | 2813 lines | 1349 code | 366 blank | 1098 comment | 404 complexity | 63e764a636fdbff2bbe32b1486573fbf MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1, GPL-3.0, MIT

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Base class for elFinder volume.
  4. * Provide 2 layers:
  5. * 1. Public API (commands)
  6. * 2. abstract fs API
  7. *
  8. * All abstract methods begin with "_"
  9. *
  10. * @author Dmitry (dio) Levashov
  11. * @author Troex Nevelin
  12. * @author Alexey Sukhotin
  13. **/
  14. abstract class elFinderVolumeDriver {
  15. /**
  16. * Driver id
  17. * Must be started from letter and contains [a-z0-9]
  18. * Used as part of volume id
  19. *
  20. * @var string
  21. **/
  22. protected $driverId = 'a';
  23. /**
  24. * Volume id - used as prefix for files hashes
  25. *
  26. * @var string
  27. **/
  28. protected $id = '';
  29. /**
  30. * Flag - volume "mounted" and available
  31. *
  32. * @var bool
  33. **/
  34. protected $mounted = false;
  35. /**
  36. * Root directory path
  37. *
  38. * @var string
  39. **/
  40. protected $root = '';
  41. /**
  42. * Root basename | alias
  43. *
  44. * @var string
  45. **/
  46. protected $rootName = '';
  47. /**
  48. * Default directory to open
  49. *
  50. * @var string
  51. **/
  52. protected $startPath = '';
  53. /**
  54. * Base URL
  55. *
  56. * @var string
  57. **/
  58. protected $URL = '';
  59. /**
  60. * Thumbnails dir path
  61. *
  62. * @var string
  63. **/
  64. protected $tmbPath = '';
  65. /**
  66. * Is thumbnails dir writable
  67. *
  68. * @var bool
  69. **/
  70. protected $tmbPathWritable = false;
  71. /**
  72. * Thumbnails base URL
  73. *
  74. * @var string
  75. **/
  76. protected $tmbURL = '';
  77. /**
  78. * undocumented class variable
  79. *
  80. * @var string
  81. **/
  82. protected $tmbSize = 48;
  83. /**
  84. * Image manipulation lib name
  85. * auto|imagick|mogtify|gd
  86. *
  87. * @var string
  88. **/
  89. protected $imgLib = 'auto';
  90. /**
  91. * Library to crypt files name
  92. *
  93. * @var string
  94. **/
  95. protected $cryptLib = '';
  96. /**
  97. * undocumented class variable
  98. *
  99. * @var string
  100. **/
  101. protected $archivers = array(
  102. 'create' => array(),
  103. 'extract' => array()
  104. );
  105. /**
  106. * How many subdirs levels return for tree
  107. *
  108. * @var int
  109. **/
  110. protected $treeDeep = 1;
  111. /**
  112. * Errors from last failed action
  113. *
  114. * @var array
  115. **/
  116. protected $error = array();
  117. /**
  118. * Today 24:00 timestamp
  119. *
  120. * @var int
  121. **/
  122. protected $today = 0;
  123. /**
  124. * Yesterday 24:00 timestamp
  125. *
  126. * @var int
  127. **/
  128. protected $yesterday = 0;
  129. /**
  130. * Object configuration
  131. *
  132. * @var array
  133. **/
  134. protected $options = array(
  135. 'id' => '',
  136. // root directory path
  137. 'path' => '',
  138. // open this path on initial request instead of root path
  139. 'startPath' => '',
  140. // how many subdirs levels return per request
  141. 'treeDeep' => 1,
  142. // root url, not set to disable sending URL to client (replacement for old "fileURL" option)
  143. 'URL' => '',
  144. // directory separator. required by client to show paths correctly
  145. 'separator' => DIRECTORY_SEPARATOR,
  146. // library to crypt/uncrypt files names (not implemented)
  147. 'cryptLib' => '',
  148. // how to detect files mimetypes. (auto/internal/finfo/mime_content_type)
  149. 'mimeDetect' => 'auto',
  150. // mime.types file path (for mimeDetect==internal)
  151. 'mimefile' => '',
  152. // directory for thumbnails
  153. 'tmbPath' => '.tmb',
  154. // mode to create thumbnails dir
  155. 'tmbPathMode' => 0777,
  156. // thumbnails dir URL. Set it if store thumbnails outside root directory
  157. 'tmbURL' => '',
  158. // thumbnails size (px)
  159. 'tmbSize' => 48,
  160. // thumbnails crop (true - crop, false - scale image to fit thumbnail size)
  161. 'tmbCrop' => true,
  162. // thumbnails background color (hex #rrggbb or 'transparent')
  163. 'tmbBgColor' => '#ffffff',
  164. // image manipulations library
  165. 'imgLib' => 'auto',
  166. // how frequiently clean thumbnails dir (0 - never, 100 - every init request)
  167. 'tmbCleanProb' => 0,
  168. // on paste file - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
  169. 'copyOverwrite' => true,
  170. // if true - join new and old directories content on paste
  171. 'copyJoin' => true,
  172. // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
  173. 'uploadOverwrite' => true,
  174. // mimetypes allowed to upload
  175. 'uploadAllow' => array('all'),
  176. // mimetypes not allowed to upload
  177. 'uploadDeny' => array(),
  178. // order to proccess uploadAllow and uploadAllow options
  179. 'uploadOrder' => 'deny,allow',
  180. // maximum upload file size. NOTE - this is size for every uploaded files
  181. 'uploadMaxSize' => 0,
  182. // files dates format
  183. 'dateFormat' => 'j M Y H:i',
  184. // files time format
  185. 'timeFormat' => 'H:i',
  186. // allow to copy from this volume to other ones?
  187. 'copyFrom' => true,
  188. // allow to copy from other volumes to this one?
  189. 'copyTo' => true,
  190. // list of commands disabled on this root
  191. 'disabled' => array(),
  192. // regexp or function name to validate new file name
  193. 'acceptedName' => '/^[^\.]/',
  194. // function/class method to control files permissions
  195. 'accessControl' => null,
  196. // some data required by access control
  197. 'accessControlData' => null,
  198. // default permissions. not set hidden/locked here - take no effect
  199. 'defaults' => array(
  200. 'read' => true,
  201. 'write' => true
  202. ),
  203. // files attributes
  204. 'attributes' => array(),
  205. // Allowed archive's mimetypes to create. Leave empty for all available types.
  206. 'archiveMimes' => array(),
  207. // Manual config for archivers. See example below. Leave empty for auto detect
  208. 'archivers' => array(),
  209. // required to fix bug on macos
  210. 'utf8fix' => false,
  211. // й ё Й Ё Ø Å
  212. 'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"),
  213. 'utf8replace' => array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5")
  214. );
  215. /**
  216. * Defaults permissions
  217. *
  218. * @var array
  219. **/
  220. protected $defaults = array(
  221. 'read' => true,
  222. 'write' => true,
  223. 'locked' => false,
  224. 'hidden' => false
  225. );
  226. /**
  227. * Access control function/class
  228. *
  229. * @var mixed
  230. **/
  231. protected $attributes = array();
  232. /**
  233. * Access control function/class
  234. *
  235. * @var mixed
  236. **/
  237. protected $access = null;
  238. /**
  239. * Mime types allowed to upload
  240. *
  241. * @var array
  242. **/
  243. protected $uploadAllow = array();
  244. /**
  245. * Mime types denied to upload
  246. *
  247. * @var array
  248. **/
  249. protected $uploadDeny = array();
  250. /**
  251. * Order to validate uploadAllow and uploadDeny
  252. *
  253. * @var array
  254. **/
  255. protected $uploadOrder = array();
  256. /**
  257. * Maximum allowed upload file size.
  258. * Set as number or string with unit - "10M", "500K", "1G"
  259. *
  260. * @var int|string
  261. **/
  262. protected $uploadMaxSize = 0;
  263. /**
  264. * Mimetype detect method
  265. *
  266. * @var string
  267. **/
  268. protected $mimeDetect = 'auto';
  269. /**
  270. * Flag - mimetypes from externail file was loaded
  271. *
  272. * @var bool
  273. **/
  274. private static $mimetypesLoaded = false;
  275. /**
  276. * Finfo object for mimeDetect == 'finfo'
  277. *
  278. * @var object
  279. **/
  280. protected $finfo = null;
  281. /**
  282. * undocumented class variable
  283. *
  284. * @var string
  285. **/
  286. protected $diabled = array();
  287. /**
  288. * default extensions/mimetypes for mimeDetect == 'internal'
  289. *
  290. * @var array
  291. **/
  292. protected static $mimetypes = array(
  293. // applications
  294. 'ai' => 'application/postscript',
  295. 'eps' => 'application/postscript',
  296. 'exe' => 'application/x-executable',
  297. 'doc' => 'application/vnd.ms-word',
  298. 'xls' => 'application/vnd.ms-excel',
  299. 'ppt' => 'application/vnd.ms-powerpoint',
  300. 'pps' => 'application/vnd.ms-powerpoint',
  301. 'pdf' => 'application/pdf',
  302. 'xml' => 'application/xml',
  303. 'odt' => 'application/vnd.oasis.opendocument.text',
  304. 'swf' => 'application/x-shockwave-flash',
  305. 'torrent' => 'application/x-bittorrent',
  306. 'jar' => 'application/x-jar',
  307. // archives
  308. 'gz' => 'application/x-gzip',
  309. 'tgz' => 'application/x-gzip',
  310. 'bz' => 'application/x-bzip2',
  311. 'bz2' => 'application/x-bzip2',
  312. 'tbz' => 'application/x-bzip2',
  313. 'zip' => 'application/zip',
  314. 'rar' => 'application/x-rar',
  315. 'tar' => 'application/x-tar',
  316. '7z' => 'application/x-7z-compressed',
  317. // texts
  318. 'txt' => 'text/plain',
  319. 'php' => 'text/x-php',
  320. 'html' => 'text/html',
  321. 'htm' => 'text/html',
  322. 'js' => 'text/javascript',
  323. 'css' => 'text/css',
  324. 'rtf' => 'text/rtf',
  325. 'rtfd' => 'text/rtfd',
  326. 'py' => 'text/x-python',
  327. 'java' => 'text/x-java-source',
  328. 'rb' => 'text/x-ruby',
  329. 'sh' => 'text/x-shellscript',
  330. 'pl' => 'text/x-perl',
  331. 'xml' => 'text/xml',
  332. 'sql' => 'text/x-sql',
  333. 'c' => 'text/x-csrc',
  334. 'h' => 'text/x-chdr',
  335. 'cpp' => 'text/x-c++src',
  336. 'hh' => 'text/x-c++hdr',
  337. 'log' => 'text/plain',
  338. 'csv' => 'text/x-comma-separated-values',
  339. // images
  340. 'bmp' => 'image/x-ms-bmp',
  341. 'jpg' => 'image/jpeg',
  342. 'jpeg' => 'image/jpeg',
  343. 'gif' => 'image/gif',
  344. 'png' => 'image/png',
  345. 'tif' => 'image/tiff',
  346. 'tiff' => 'image/tiff',
  347. 'tga' => 'image/x-targa',
  348. 'psd' => 'image/vnd.adobe.photoshop',
  349. 'ai' => 'image/vnd.adobe.photoshop',
  350. 'xbm' => 'image/xbm',
  351. 'pxm' => 'image/pxm',
  352. //audio
  353. 'mp3' => 'audio/mpeg',
  354. 'mid' => 'audio/midi',
  355. 'ogg' => 'audio/ogg',
  356. 'oga' => 'audio/ogg',
  357. 'm4a' => 'audio/x-m4a',
  358. 'wav' => 'audio/wav',
  359. 'wma' => 'audio/x-ms-wma',
  360. // video
  361. 'avi' => 'video/x-msvideo',
  362. 'dv' => 'video/x-dv',
  363. 'mp4' => 'video/mp4',
  364. 'mpeg' => 'video/mpeg',
  365. 'mpg' => 'video/mpeg',
  366. 'mov' => 'video/quicktime',
  367. 'wm' => 'video/x-ms-wmv',
  368. 'flv' => 'video/x-flv',
  369. 'mkv' => 'video/x-matroska',
  370. 'webm' => 'video/webm',
  371. 'ogv' => 'video/ogg',
  372. 'ogm' => 'video/ogg'
  373. );
  374. /**
  375. * undocumented class variable
  376. *
  377. * @var string
  378. **/
  379. protected $separator = DIRECTORY_SEPARATOR;
  380. /*********************************************************************/
  381. /* INITIALIZATION */
  382. /*********************************************************************/
  383. /**
  384. * Prepare driver before mount volume.
  385. * Return true if volume is ready.
  386. *
  387. * @return bool
  388. * @author Dmitry (dio) Levashov
  389. **/
  390. protected function init() {
  391. return true;
  392. }
  393. /**
  394. * Configure after successfull mount.
  395. * By default set thumbnails path and image manipulation library.
  396. *
  397. * @return void
  398. * @author Dmitry (dio) Levashov
  399. **/
  400. protected function configure() {
  401. // set thumbnails path
  402. $path = $this->options['tmbPath'];
  403. if ($path) {
  404. if (!file_exists($path)) {
  405. if (@mkdir($path)) {
  406. chmod($path, $this->options['tmbPathMode']);
  407. } else {
  408. $path = '';
  409. }
  410. }
  411. if (is_dir($path) && is_readable($path)) {
  412. $this->tmbPath = $path;
  413. $this->tmbPathWritable = is_writable($path);
  414. }
  415. }
  416. // set image manipulation library
  417. $type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib'])
  418. ? strtolower($this->options['imgLib'])
  419. : 'auto';
  420. if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) {
  421. $this->imgLib = 'imagick';
  422. } else {
  423. $this->imgLib = function_exists('gd_info') ? 'gd' : '';
  424. }
  425. // clean thumbnails dir
  426. if ($this->tmbPath) {
  427. srand((double) microtime() * 1000000);
  428. if (rand(1, 200) <= $this->options['tmbCleanProb']) {
  429. $ls = scandir($this->tmbPath);
  430. for ($i=0, $s = count($ls); $i < $s; $i++) {
  431. $pinfo = pathinfo($ls[$i]);
  432. if (strtolower($pinfo['extension']) == 'png') {
  433. @unlink($this->tmbPath.DIRECTORY_SEPARATOR.$ls[$i]);
  434. }
  435. }
  436. }
  437. }
  438. }
  439. /*********************************************************************/
  440. /* PUBLIC API */
  441. /*********************************************************************/
  442. /**
  443. * Return driver id
  444. *
  445. * @return string
  446. * @author Dmitry (dio) Levashov
  447. **/
  448. public function driverId() {
  449. return $this->driverId;
  450. }
  451. /**
  452. * Return volume id
  453. *
  454. * @return string
  455. * @author Dmitry (dio) Levashov
  456. **/
  457. public function id() {
  458. return $this->id;
  459. }
  460. /**
  461. * undocumented function
  462. *
  463. * @return void
  464. * @author Dmitry Levashov
  465. **/
  466. public function name() {
  467. return strtolower(substr(get_class($this), strlen('elfinderdriver')));
  468. }
  469. /**
  470. * Return debug info for client
  471. *
  472. * @return array
  473. * @author Dmitry (dio) Levashov
  474. **/
  475. public function debug() {
  476. return array(
  477. 'mimeDetect' => $this->mimeDetect,
  478. 'imgLib' => $this->imgLib
  479. );
  480. }
  481. /**
  482. * "Mount" volume.
  483. * Return true if volume available for read or write,
  484. * false - otherwise
  485. *
  486. * @return bool
  487. * @author Dmitry (dio) Levashov
  488. * @author Alexey Sukhotin
  489. **/
  490. public function mount(array $opts) {
  491. if (empty($opts['path'])) {
  492. return false;
  493. }
  494. $this->options = array_merge($this->options, $opts);
  495. $this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
  496. $this->root = $this->_normpath($this->options['path']);
  497. $this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
  498. // default file attribute
  499. $this->defaults = array(
  500. 'read' => isset($this->options['defaults']['read']) ? !!$this->options['defaults']['read'] : true,
  501. 'write' => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
  502. 'locked' => false,
  503. 'hidden' => false
  504. );
  505. // root attributes
  506. $this->attributes[] = array(
  507. 'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
  508. 'locked' => true,
  509. 'hidden' => false
  510. );
  511. // set files attributes
  512. if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) {
  513. foreach ($this->options['attributes'] as $a) {
  514. // attributes must contain pattern and at least one rule
  515. if (!empty($a['pattern']) || count($a) > 1) {
  516. $this->attributes[] = $a;
  517. }
  518. }
  519. }
  520. if (!empty($this->options['accessControl'])) {
  521. if (is_string($this->options['accessControl'])
  522. && function_exists($this->options['accessControl'])) {
  523. $this->access = $this->options['accessControl'];
  524. } elseif (is_array($this->options['accessControl'])
  525. && count($this->options['accessControl']) > 1
  526. && is_object($this->options['accessControl'][0])
  527. && method_exists($this->options['accessControl'][0], $this->options['accessControl'][1])) {
  528. $this->access = array($this->options['accessControl'][0], $this->options['accessControl'][1]);
  529. }
  530. }
  531. // debug($this->attributes);
  532. if (!$this->init()) {
  533. return false;
  534. }
  535. $this->today = mktime(0,0,0, date('m'), date('d'), date('Y'));
  536. $this->yesterday = $this->today-86400;
  537. // check some options is arrays
  538. $this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
  539. ? $this->options['uploadAllow']
  540. : array();
  541. $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
  542. ? $this->options['uploadDeny']
  543. : array();
  544. $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
  545. $this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
  546. if (!empty($this->options['uploadMaxSize'])) {
  547. $size = ''.$this->options['uploadMaxSize'];
  548. $unit = strtolower(substr($size, strlen($size) - 1));
  549. $n = 1;
  550. switch ($unit) {
  551. case 'k':
  552. $n = 1024;
  553. break;
  554. case 'm':
  555. $n = 1048576;
  556. break;
  557. case 'g':
  558. $n = 1073741824;
  559. }
  560. $this->uploadMaxSize = intval($size)*$n;
  561. }
  562. $this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
  563. ? $this->options['disabled']
  564. : array();
  565. $this->cryptLib = $this->options['cryptLib'];
  566. $this->mimeDetect = $this->options['mimeDetect'];
  567. // find available mimetype detect method
  568. $type = strtolower($this->options['mimeDetect']);
  569. $type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
  570. $regexp = '/text\/x\-(php|c\+\+)/';
  571. if (($type == 'finfo' || $type == 'auto')
  572. && class_exists('finfo')
  573. && preg_match($regexp, array_shift(explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__))))) {
  574. $type = 'finfo';
  575. } elseif (($type == 'mime_content_type' || $type == 'auto')
  576. && function_exists('mime_content_type')
  577. && preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) {
  578. $type = 'mime_content_type';
  579. } else {
  580. $type = 'internal';
  581. }
  582. $this->mimeDetect = $type;
  583. // load mimes from external file for mimeDetect == 'internal'
  584. // based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
  585. // file must be in file directory or in parent one
  586. if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
  587. self::$mimetypesLoaded = true;
  588. $this->mimeDetect = 'internal';
  589. $file = false;
  590. if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
  591. $file = $this->options['mimefile'];
  592. } elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
  593. $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
  594. } elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
  595. $file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
  596. }
  597. if ($file && file_exists($file)) {
  598. $mimecf = file($file);
  599. foreach ($mimecf as $line_num => $line) {
  600. if (!preg_match('/^\s*#/', $line)) {
  601. $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
  602. for ($i = 1, $size = count($mime); $i < $size ; $i++) {
  603. if (!isset(self::$mimetypes[$mime[$i]])) {
  604. self::$mimetypes[$mime[$i]] = $mime[0];
  605. } else {
  606. // echo $mime[$i].' '.$mime[0].'<br>';
  607. }
  608. }
  609. }
  610. }
  611. }
  612. }
  613. // debug(self::$mimetypes);
  614. // set root path
  615. if (!$this->_isDir($this->root)) {
  616. return false;
  617. }
  618. $read = $this->attr($this->root, 'read');
  619. // echo $this->attr($this->root.'/.tmb', 'hidden');
  620. if (!$read && !$this->attr($this->root, 'write')) {
  621. return false;
  622. }
  623. if ($read) {
  624. // check startPath - path to open by default instead of root
  625. if ($this->options['startPath']) {
  626. $path = $this->_normpath($this->options['startPath']);
  627. if ($this->_isDir($path)
  628. && $this->attr($path, 'read')
  629. && !$this->attr($path, 'hidden')
  630. && $this->_inpath($path, $this->root)) {
  631. $this->startPath = $path;
  632. }
  633. }
  634. } else {
  635. $this->options['URL'] = '';
  636. $this->options['tmbURL'] = '';
  637. $this->options['tmbPath'] = '';
  638. // read only volume
  639. array_unshift($this->attributes, array(
  640. 'pattern' => '/.*/',
  641. 'read' => false
  642. ));
  643. }
  644. $this->rootName = empty($this->options['alias']) ? $this->_basename($this->root) : $this->options['alias'];
  645. $this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
  646. $this->tmbSize = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
  647. $this->URL = $this->options['URL'];
  648. if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
  649. $this->URL .= '/';
  650. }
  651. $this->tmbURL = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
  652. if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
  653. $this->tmbURL .= '/';
  654. }
  655. $this->nameValidator = is_string($this->options['acceptedName']) && !empty($this->options['acceptedName'])
  656. ? $this->options['acceptedName']
  657. : '';
  658. $this->_checkArchivers();
  659. // manual control archive types to create
  660. if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) {
  661. foreach ($this->archivers['create'] as $mime => $v) {
  662. if (!in_array($mime, $this->options['archiveMimes'])) {
  663. unset($this->archivers['create'][$mime]);
  664. }
  665. }
  666. }
  667. // manualy add archivers
  668. if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) {
  669. foreach ($this->options['archivers']['create'] as $mime => $conf) {
  670. if (strpos($mime, 'application/') === 0
  671. && !empty($conf['cmd'])
  672. && isset($conf['argc'])
  673. && !empty($conf['ext'])
  674. && !isset($this->archivers['create'][$mime])) {
  675. $this->archivers['create'][$mime] = $conf;
  676. }
  677. }
  678. }
  679. if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) {
  680. foreach ($this->options['archivers']['extract'] as $mime => $conf) {
  681. if (substr($mime, 'application/') === 0
  682. && !empty($cons['cmd'])
  683. && isset($conf['argc'])
  684. && !empty($conf['ext'])
  685. && !isset($this->archivers['extract'][$mime])) {
  686. $this->archivers['extract'][$mime] = $conf;
  687. }
  688. }
  689. }
  690. $this->configure();
  691. return $this->mounted = true;
  692. }
  693. /**
  694. * Return error message from last failed action
  695. *
  696. * @return array
  697. * @author Dmitry (dio) Levashov
  698. **/
  699. public function error() {
  700. return $this->error;
  701. }
  702. /**
  703. * Return root folder hash
  704. *
  705. * @return string
  706. * @author Dmitry (dio) Levashov
  707. **/
  708. public function root() {
  709. return $this->encode($this->root);
  710. }
  711. /**
  712. * Return root or startPath hash
  713. *
  714. * @return string
  715. * @author Dmitry (dio) Levashov
  716. **/
  717. public function defaultPath() {
  718. return $this->encode($this->startPath ? $this->startPath : $this->root);
  719. }
  720. /**
  721. * Return file path started from root dir
  722. *
  723. * @param string $hash file hash
  724. * @return string
  725. * @author Dmitry (dio) Levashov
  726. **/
  727. public function path($hash) {
  728. return $this->_path($this->decode($hash));
  729. }
  730. /**
  731. * Return volume options required by client:
  732. *
  733. * @return array
  734. * @author Dmitry (dio) Levashov
  735. **/
  736. public function options($hash) {
  737. return array(
  738. 'path' => $this->path($hash),
  739. 'url' => $this->URL,
  740. 'tmbUrl' => $this->tmbURL,
  741. 'disabled' => $this->disabled,
  742. 'separator' => $this->separator,
  743. 'copyOverwrite' => intval($this->options['copyOverwrite']),
  744. 'archivers' => array(
  745. 'create' => array_keys($this->archivers['create']),
  746. 'extract' => array_keys($this->archivers['extract'])
  747. )
  748. );
  749. }
  750. /**
  751. * Return true if mime is required mimes list
  752. *
  753. * @param string $mime mime type to check
  754. * @param array $mimes allowed mime types list
  755. * @return bool
  756. * @author Dmitry (dio) Levashov
  757. **/
  758. public function mimeAccepted($mime, $mimes=array()) {
  759. return $mime == 'directory' || empty($mimes) || in_array($mime, $mimes) || in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
  760. }
  761. /**
  762. * undocumented function
  763. *
  764. * @return void
  765. * @author Dmitry Levashov
  766. **/
  767. public function copyFromAllowed() {
  768. return !!$this->options['copyFrom'];
  769. }
  770. /**
  771. * undocumented function
  772. *
  773. * @return void
  774. * @author Dmitry Levashov
  775. **/
  776. public function copyToAllowed() {
  777. return !!$this->options['copyTo'];
  778. }
  779. /**
  780. * Return true if file exists
  781. *
  782. * @param string $hash file hash
  783. * @return bool
  784. * @author Dmitry (dio) Levashov
  785. **/
  786. public function fileExists($hash) {
  787. $path = $this->decode($hash);
  788. return $path && $this->_fileExists($path);
  789. }
  790. /**
  791. * Check if file is folder
  792. *
  793. * @param string $hash file hash
  794. * @return bool
  795. * @author Dmitry (dio) Levashov
  796. **/
  797. public function isDir($hash) {
  798. $path = $this->decode($hash);
  799. return $path && $this->_fileExists($path) && $this->_isDir($this->decode($hash));
  800. }
  801. /**
  802. * Check if file is not folder
  803. *
  804. * @param string $hash file hash
  805. * @return bool
  806. * @author Dmitry (dio) Levashov
  807. **/
  808. public function isFile($hash) {
  809. $path = $this->decode($hash);
  810. return $path && $this->_fileExists($path) && $this->_isFile($this->decode($hash));
  811. }
  812. /**
  813. * Check if file symlink
  814. *
  815. * @param string $hash file hash
  816. * @return bool
  817. * @author Dmitry (dio) Levashov
  818. **/
  819. public function isLink($hash) {
  820. $path = $this->decode($hash);
  821. return $path && $this->_fileExists($path) && $this->_isLink($this->decode($hash));
  822. }
  823. /**
  824. * Return true if folder is readable.
  825. * If hash is not set - check root folder
  826. *
  827. * @param string $hash file hash
  828. * @return bool
  829. * @author Dmitry (dio) Levashov
  830. **/
  831. public function isReadable($hash='') {
  832. $path = $hash ? $this->decode($hash) : $this->root;
  833. return $path && $this->_fileExists($path) && $this->attr($path, 'read');
  834. }
  835. /**
  836. * Return true if folder is writeable.
  837. * If hash is not set - check root folder
  838. *
  839. * @param string $hash file hash
  840. * @return bool
  841. * @author Dmitry (dio) Levashov
  842. **/
  843. public function isWritable($hash='') {
  844. $path = $hash ? $this->decode($hash) : $this->root;
  845. return $path && $this->_fileExists($path) && $this->attr($path, 'write');
  846. }
  847. /**
  848. * Return true if file is hidden
  849. *
  850. * @param string $hash file hash
  851. * @return void
  852. * @author Dmitry (dio) Levashov
  853. **/
  854. public function isHidden($hash) {
  855. $path = $this->decode($hash);
  856. return $path && $this->_fileExists($path) && $this->attr($path, 'hidden');
  857. }
  858. /**
  859. * Return true if file is locked
  860. *
  861. * @param string $hash file hash
  862. * @return void
  863. * @author Dmitry (dio) Levashov
  864. **/
  865. public function isLocked($hash) {
  866. $path = $this->decode($hash);
  867. return $path && $this->_fileExists($path) && $this->attr($path, 'locked');
  868. }
  869. /**
  870. * Return file parent folder hash
  871. *
  872. * @param string $hash file hash
  873. * @return string
  874. * @author Dmitry (dio) Levashov
  875. **/
  876. public function parent($hash) {
  877. $path = $this->decode($hash);
  878. return $path ? $this->_dirname($path) : '';
  879. }
  880. /**
  881. * Return file info or false on error
  882. *
  883. * @param string $hash file hash
  884. * @param bool $hidden return hidden file info
  885. * @return array|false
  886. * @author Dmitry (dio) Levashov
  887. **/
  888. public function file($hash, $hidden=false) {
  889. if (($file = $this->stat($this->decode($hash))) == false
  890. || !($hidden || empty($file['hidden']))) {
  891. return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
  892. }
  893. return $file;
  894. }
  895. /**
  896. * Return folder info
  897. *
  898. * @param string $hash folder hash
  899. * @param bool $hidden return hidden file info
  900. * @return array|false
  901. * @author Dmitry (dio) Levashov
  902. **/
  903. public function dir($hash, $hidden=false, $resolveLink=false) {
  904. if (($dir = $this->file($hash, $hidden)) == false) {
  905. return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
  906. }
  907. if ($dir['mime'] != 'directory') {
  908. return $this->setError(elFinder::ERROR_NOT_DIR);
  909. }
  910. if ($resolveLink && !empty($dir['alias'])) {
  911. if (!($target = $this->_readlink($this->decode($hash)))) {
  912. return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
  913. }
  914. if (!($dir = $this->stat($target)) || $this->attr($target, 'hidden')) {
  915. return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
  916. }
  917. if ($dir['mime'] != 'directory') {
  918. return $this->setError(elFinder::ERROR_NOT_DIR);
  919. }
  920. }
  921. return $dir;
  922. return $dir['mime'] == 'directory'
  923. ? $dir
  924. : $this->setError(elFinder::ERROR_NOT_DIR);
  925. }
  926. /**
  927. * Return directory content or false on error
  928. *
  929. * @param string $hash file hash
  930. * @param array $mimes allowed mimetypes list
  931. * @return array|false
  932. * @author Dmitry (dio) Levashov
  933. **/
  934. public function scandir($hash, $mimes=array()) {
  935. if (($dir = $this->dir($hash)) == false) {
  936. return false;
  937. }
  938. return $dir['read']
  939. ? $this->getScandir($this->decode($hash), $mimes)
  940. : $this->setError(elFinder::ERROR_PERM_DENIED);
  941. }
  942. /**
  943. * Return dir files names list
  944. *
  945. * @param string $hash file hash
  946. * @param array $mimes allowed mimetypes list
  947. * @return array
  948. * @author Dmitry (dio) Levashov
  949. **/
  950. public function ls($hash, $mimes=array()) {
  951. if (($dir = $this->dir($hash)) == false) {
  952. return false;
  953. }
  954. if (!$dir['read']) {
  955. $this->setError(elFinder::ERROR_PERM_DENIED);
  956. }
  957. $list = array();
  958. foreach ($this->_scandir($this->decode($hash)) as $p) {
  959. if (!$this->attr($p, 'hidden')
  960. && ($this->_isDir($p) || !$mimes || $this->mimeAccepted($this->mimetype($p), $mimes))) {
  961. $list[] = $this->_basename($p);
  962. }
  963. }
  964. return $list;
  965. }
  966. /**
  967. * Return subfolders for required one or false on error
  968. *
  969. * @param string $hash folder hash or empty string to get tree from root folder
  970. * @param int $deep subdir deep
  971. * @param string $exclude dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
  972. * @return array|false
  973. * @author Dmitry (dio) Levashov
  974. **/
  975. public function tree($hash='', $deep=0, $exclude='') {
  976. $hash = $hash ? $hash : $this->encode($this->root);
  977. if (($dir = $this->dir($hash)) == false) {
  978. return false;
  979. }
  980. if (!$dir['read']) {
  981. return $this->setError(elFinder::ERROR_PERM_DENIED);
  982. }
  983. $path = $this->decode($hash);
  984. $dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $this->decode($exclude));
  985. array_unshift($dirs, $dir);
  986. return $dirs;
  987. }
  988. /**
  989. * Return part of dirs tree from required dir upside till root dir
  990. *
  991. * @param string $hash directory hash
  992. * @return array
  993. * @author Dmitry (dio) Levashov
  994. **/
  995. public function parents($hash) {
  996. if (($current = $this->dir($hash)) == false) {
  997. return false;
  998. }
  999. $path = $this->decode($hash);
  1000. $tree = array();
  1001. while ($path && $path != $this->root) {
  1002. $path = $this->_dirname($path);
  1003. if ($this->attr($path, 'hidden')) {
  1004. return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
  1005. }
  1006. if (!$this->attr($path, 'read')) {
  1007. return $this->setError(elFinder::ERROR_OPEN, $this->_basename($path), '<br>', elFinder::ERROR_PERM_DENIED);
  1008. }
  1009. $dir = $this->stat($path);
  1010. array_unshift($tree, $dir);
  1011. if ($path != $this->root) {
  1012. foreach ($this->gettree($path, 0) as $dir) {
  1013. if (!in_array($dir, $tree)) {
  1014. $tree[] = $dir;
  1015. }
  1016. }
  1017. }
  1018. }
  1019. return $tree ? $tree : array($current);
  1020. }
  1021. /**
  1022. * Create thumbnail for required file and return its name of false on failed
  1023. *
  1024. * @return string|false
  1025. * @author Dmitry (dio) Levashov
  1026. **/
  1027. public function tmb($hash) {
  1028. if ($path = $this->decode($hash)) {
  1029. return ($tmb = $this->gettmb($path)) ? $tmb : $this->createTmb($path);
  1030. }
  1031. return false;
  1032. }
  1033. /**
  1034. * Return file size / total directory size
  1035. *
  1036. * @param string file hash
  1037. * @return int
  1038. * @author Dmitry (dio) Levashov
  1039. **/
  1040. public function size($hash) {
  1041. return $this->countSize($this->decode($hash));
  1042. }
  1043. /**
  1044. * Open file for reading and return file pointer
  1045. *
  1046. * @param string file hash
  1047. * @return Resource
  1048. * @author Dmitry (dio) Levashov
  1049. **/
  1050. public function open($hash) {
  1051. $path = $this->decode($hash);
  1052. if (($file = $this->file($hash)) == false) {
  1053. return false;
  1054. }
  1055. if ($file['mime'] == 'directory') {
  1056. return false;
  1057. }
  1058. return $this->_fopen($path, 'rb');
  1059. }
  1060. /**
  1061. * Close file pointer
  1062. *
  1063. * @param Resource $fp file pointer
  1064. * @param string $hash file hash
  1065. * @return void
  1066. * @author Dmitry (dio) Levashov
  1067. **/
  1068. public function close($fp, $hash) {
  1069. $this->_fclose($fp, $this->decode($hash));
  1070. }
  1071. /**
  1072. * Create directory and return dir info
  1073. *
  1074. * @param string $dst destination directory
  1075. * @param string $name directory name
  1076. * @return array|false
  1077. * @author Dmitry (dio) Levashov
  1078. **/
  1079. public function mkdir($dst, $name, $copy=false) {
  1080. $path = $this->decode($dst);
  1081. if (($dir = $this->dir($dst)) == false) {
  1082. return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
  1083. }
  1084. if (!$dir['write']) {
  1085. return $this->setError(elFinder::ERROR_PERM_DENIED);
  1086. }
  1087. if (!$this->nameAccepted($name)) {
  1088. return $this->setError(elFinder::ERROR_INVALID_NAME);
  1089. }
  1090. if ($copy && !$this->options['copyOverwrite']) {
  1091. $name = $this->uniqueName($path, $name, '-', false);
  1092. }
  1093. $dst = $this->_joinPath($path, $name);
  1094. if ($this->_fileExists($dst)) {
  1095. if ($copy) {
  1096. if (!$this->options['copyJoin'] && $this->attr($dst, 'write')) {
  1097. foreach ($this->_scandir($dst) as $p) {
  1098. $this->doRm($p);
  1099. }
  1100. }
  1101. return $this->stat($dst);
  1102. }
  1103. return $this->setError(elFinder::ERROR_EXISTS, $name);
  1104. }
  1105. return $this->_mkdir($path, $name) ? $this->stat($this->_joinPath($path, $name)) : false;
  1106. }
  1107. /**
  1108. * Create empty file and return its info
  1109. *
  1110. * @param string $dst destination directory
  1111. * @param string $name file name
  1112. * @return array|false
  1113. * @author Dmitry (dio) Levashov
  1114. **/
  1115. public function mkfile($dst, $name) {
  1116. $path = $this->decode($dst);
  1117. if (($dir = $this->dir($dst, true)) == false) {
  1118. return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#dst');
  1119. }
  1120. if (!$dir['write']) {
  1121. return $this->setError(elFinder::ERROR_PERM_DENIED);
  1122. }
  1123. if (!$this->nameAccepted($name)) {
  1124. return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
  1125. }
  1126. if ($this->_fileExists($this->_joinPath($path, $name))) {
  1127. return $this->setError(elFinder::ERROR_EXISTS, $name);
  1128. }
  1129. return $this->_mkfile($path, $name) ? $this->stat($this->_joinPath($path, $name)) : false;
  1130. }
  1131. /**
  1132. * Rename file and return file info
  1133. *
  1134. * @param string $hash file hash
  1135. * @param string $name new file name
  1136. * @return array|false
  1137. * @author Dmitry (dio) Levashov
  1138. **/
  1139. public function rename($hash, $name) {
  1140. $path = $this->decode($hash);
  1141. if (!($file = $this->file($hash))) {
  1142. return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
  1143. }
  1144. $dir = $this->_dirname($path);
  1145. if ($this->attr($path, 'locked')) {
  1146. return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
  1147. }
  1148. if (!$this->nameAccepted($name)) {
  1149. return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
  1150. }
  1151. if ($name == $file['name']) {
  1152. return $file;
  1153. }
  1154. $dst = $this->_joinPath($dir, $name);
  1155. if ($this->_fileExists($dst)) {
  1156. return $this->setError(elFinder::ERROR_EXISTS, $file['name']);
  1157. }
  1158. if ($this->_move($path, $dir, $name)) {
  1159. $this->rmTmb($path);
  1160. return $this->stat($this->_joinPath($dir, $name));
  1161. }
  1162. return false;
  1163. }
  1164. /**
  1165. * Create file copy with suffix "copy number" and return its info
  1166. *
  1167. * @param string $hash file hash
  1168. * @return array|false
  1169. * @author Dmitry (dio) Levashov
  1170. **/
  1171. public function duplicate($hash) {
  1172. if (($file = $this->file($hash)) == false) {
  1173. return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
  1174. }
  1175. $path = $this->decode($hash);
  1176. $dir = $this->_dirname($path);
  1177. return ($path = $this->doCopy($path, $dir, $this->uniqueName($dir, $file['name']))) == false
  1178. ? false
  1179. : $this->stat($path);
  1180. }
  1181. /**
  1182. * Return true if file mime type accepted for upload
  1183. *
  1184. * @param string $tmpPath temporary file path
  1185. * @param string $name file name
  1186. * @return bool
  1187. * @author Dmitry (dio) Levashov
  1188. **/
  1189. public function uploadAllow($tmpPath, $name) {
  1190. if (!$this->nameAccepted($name)) {
  1191. return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
  1192. }
  1193. $mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpPath);
  1194. $allow = in_array('all', $this->uploadAllow) || $this->mimeAccepted($mime, $this->uploadAllow);
  1195. $deny = in_array('all', $this->uploadDeny) || $this->mimeAccepted($mime, $this->uploadDeny);
  1196. // dont ask me what this mean. I forgot it, but its work :)
  1197. // for details see python connector
  1198. if (!($this->uploadOrder[0] == 'allow' ? $allow && !$deny : $allow || !$deny)) {
  1199. return $this->setError(elFinder::ERROR_MIME);
  1200. }
  1201. if ($this->uploadMaxSize > 0 && filesize($tmpPath) > $this->uploadMaxSize) {
  1202. return $this->setError(elFinder::ERROR_UPLOAD_SIZE);
  1203. }
  1204. return true;
  1205. }
  1206. /**
  1207. * Save file in required directory.
  1208. *
  1209. * @param resource $fp file pointer
  1210. * @param string $dst destination directory
  1211. * @param string $name file name
  1212. * @param string $cmd source command name
  1213. * @return array
  1214. * @author Dmitry (dio) Levashov
  1215. **/
  1216. public function save($fp, $dst, $name, $cmd='upload') {
  1217. if (($dir = $this->dir($dst, true, true)) == false) {
  1218. return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
  1219. }
  1220. if (!$dir['write']) {
  1221. return $this->setError(elFinder::ERROR_PERM_DENIED);
  1222. }
  1223. if (!$this->nameAccepted($name)) {
  1224. return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
  1225. }
  1226. $dst = $this->decode($dst);
  1227. $_dst = $this->_joinPath($dst, $name);
  1228. if ($this->_fileExists($_dst)) {
  1229. if (($cmd == 'upload' && !$this->options['uploadOverwrite'])
  1230. || ($cmd == 'copy' && !$this->options['copyOverwrite'])) {
  1231. $name = $this->uniqueName($dst, $name, '-', false);
  1232. } elseif (!$this->attr($_dst, 'write')) {
  1233. return $this->setError(elFinder::ERROR_PERM_DENIED);
  1234. } elseif (!$this->doRm($_dst)) {
  1235. return false;
  1236. }
  1237. }
  1238. return ($path = $this->_save($fp, $dst, $name))
  1239. ? $this->stat($path)
  1240. : false;
  1241. }
  1242. /**
  1243. * Return file contents
  1244. *
  1245. * @param string $hash file hash
  1246. * @return string|false
  1247. * @author Dmitry (dio) Levashov
  1248. **/
  1249. public function getContents($hash) {
  1250. $file = $this->file($hash);
  1251. if (!$file) {
  1252. return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
  1253. }
  1254. if ($file['mime'] == 'directory') {
  1255. return $this->setError(elFinder::ERROR_NOT_FILE);
  1256. }
  1257. if (!$file['read']) {
  1258. return $this->setError(elFinder::ERROR_PERM_DENIED);
  1259. }
  1260. return $this->_getContents($this->decode($hash));
  1261. }
  1262. /**
  1263. * Put content in text file and return file info.
  1264. *
  1265. * @param string $hash file hash
  1266. * @param string $content new file content
  1267. * @return array
  1268. * @author Dmitry (dio) Levashov
  1269. **/
  1270. public function putContents($hash, $content) {
  1271. $path = $this->decode($hash);
  1272. if (!($file = $this->file($hash))) {
  1273. return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
  1274. }
  1275. if (!$file['write']) {
  1276. return $this->setError(elFinder::ERROR_PERM_DENIED);
  1277. }
  1278. return $this->_filePutContents($path, $content) ? $this->stat($path) : false;
  1279. }
  1280. /**
  1281. * Extract files from archive
  1282. *
  1283. * @param string $hash archive hash
  1284. * @return array|bool
  1285. * @author Dmitry (dio) Levashov,
  1286. * @author Alexey Sukhotin
  1287. **/
  1288. public function extract($hash) {
  1289. if (($file = $this->file($hash)) == false) {
  1290. return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
  1291. }
  1292. $archiver = isset($this->archivers['extract'][$file['mime']])
  1293. ? $this->archivers['extract'][$file['mime']]
  1294. : false;
  1295. if (!$archiver) {
  1296. return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
  1297. }
  1298. $path = $this->decode($hash);
  1299. if (!$file['read'] || !$this->attr($this->_dirname($path), 'write')) {
  1300. return $this->setError(elFinder::ERROR_PERM_DENIED);
  1301. }
  1302. $before = $this->scandir($file['phash']);
  1303. if (!$this->_extract($path, $archiver)) {
  1304. return false;
  1305. }
  1306. $after = $this->scandir($file['phash']);
  1307. $diff = array();
  1308. foreach ($after as $file) {
  1309. if (!in_array($file, $before)) {
  1310. $diff[] = $file;
  1311. }
  1312. }
  1313. return $diff;
  1314. }
  1315. /**
  1316. * Add files to archive
  1317. *
  1318. * @return void
  1319. **/
  1320. public function archive($hashes, $mime) {
  1321. $archiver = isset($this->archivers['create'][$mime])
  1322. ? $this->archivers['create'][$mime]
  1323. : false;
  1324. if (!$archiver) {
  1325. return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
  1326. }
  1327. $files = array();
  1328. foreach ($hashes as $hash) {
  1329. if (($file = $this->file($hash)) == false) {
  1330. return $this->error(elFinder::ERROR_FILE_NOT_FOUND, '#'+$hash);
  1331. }
  1332. if (!$file['read']) {
  1333. return $this->error(elFinder::ERROR_PERM_DENIED);
  1334. }
  1335. $path = $this->decode($hash);
  1336. if (!isset($dir)) {
  1337. $dir = $this->_dirname($path);
  1338. if (!$this->attr($dir, 'write')) {
  1339. return $this->error(elFinder::ERROR_PERM_DENIED);
  1340. }
  1341. }
  1342. $files[] = $this->_basename($path);
  1343. }
  1344. $name = (count($files) == 1 ? $files[0] : 'Archive').'.'.$archiver['ext'];
  1345. $name = $this->uniqueName($dir, $name, '');
  1346. $archive = $this->_archive($dir, $files, $name, $archiver);
  1347. return $archive ? $this->stat($archive) : false;
  1348. }
  1349. /**
  1350. * Remove file/dir
  1351. *
  1352. * @param string $hash file hash
  1353. * @return bool
  1354. * @author Dmitry (dio) Levashov
  1355. **/
  1356. public function rm($hash) {
  1357. return $this->doRm($this->decode($hash));
  1358. }
  1359. /**
  1360. * undocumented function
  1361. *
  1362. * @param string $q search string
  1363. * @param array $mimes
  1364. * @return array
  1365. * @author Dmitry (dio) Levashov
  1366. **/
  1367. public function search($q, $mimes) {
  1368. return $this->doSearch($this->root, $q, $mimes);
  1369. }
  1370. /**
  1371. * Return image dimensions
  1372. *
  1373. * @param string $hash file hash
  1374. * @return array
  1375. * @author Dmitry (dio) Levashov
  1376. **/
  1377. public function dimensions($hash) {
  1378. if (($file = $this->file($hash)) == false) {
  1379. return false;
  1380. }
  1381. return $this->_dimensions($this->decode($hash), $file['mime']);
  1382. }
  1383. /**
  1384. * Save error message
  1385. *
  1386. * @param int|array error number | array(error number, arguments)
  1387. * @return false
  1388. * @author Dmitry(dio) Levashov
  1389. **/
  1390. protected function setError($error) {
  1391. $this->error = func_get_args();
  1392. return false;
  1393. }
  1394. /*********************************************************************/
  1395. /* FS API */
  1396. /*********************************************************************/
  1397. /***************** paths *******************/
  1398. /**
  1399. * Encode path into hash
  1400. *
  1401. * @param string file path
  1402. * @return string
  1403. * @author Dmitry (dio) Levashov
  1404. * @author Troex Nevelin
  1405. **/
  1406. protected function encode($path) {
  1407. if ($path) {
  1408. // cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
  1409. $p = $this->_relpath($path);
  1410. // if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
  1411. if (!$p) {
  1412. $p = DIRECTORY_SEPARATOR;
  1413. }
  1414. // TODO crypt path and return hash
  1415. $hash = $this->crypt($p);
  1416. // hash is used as id in HTML that means it must contain vaild chars
  1417. // make base64 html safe and append prefix in begining
  1418. $hash = strtr(base64_encode($hash), '+/=', '-_.');
  1419. // remove dots '.' at the end, before it was '=' in base64
  1420. $hash = rtrim($hash, '.');
  1421. // append volume id to make hash unique
  1422. return $this->id.$hash;
  1423. }
  1424. }
  1425. /**
  1426. * Decode path from hash
  1427. *
  1428. * @param string file hash
  1429. * @return string
  1430. * @author Dmitry (dio) Levashov
  1431. * @author Troex Nevelin
  1432. **/
  1433. protected function decode($hash) {
  1434. if (strpos($hash, $this->id) === 0) {
  1435. // cut volume id after it was prepended in encode
  1436. $h = substr($hash, strlen($this->id));
  1437. // replace HTML safe base64 to normal
  1438. $h = base64_decode(strtr($h, '-_.', '+/='));
  1439. // TODO uncrypt hash and return path
  1440. $path = $this->uncrypt($h);
  1441. // append ROOT to path after it was cut in encode
  1442. return $this->_abspath($path);//$this->root.($path == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR.$path);
  1443. }
  1444. }
  1445. /**
  1446. * Return crypted path
  1447. * Not implemented
  1448. *
  1449. * @param string path
  1450. * @return mixed
  1451. * @author Dmitry (dio) Levashov
  1452. **/
  1453. protected function crypt($path) {
  1454. return $path;
  1455. }
  1456. /**
  1457. * Return uncrypted path
  1458. * Not implemented
  1459. *
  1460. * @param mixed hash
  1461. * @return mixed
  1462. * @author Dmitry (dio) Levashov
  1463. **/
  1464. protected function uncrypt($hash) {
  1465. return $hash;
  1466. }
  1467. /**
  1468. * Validate file name based on $this->options['acceptedName'] regexp
  1469. *
  1470. * @param string $name file name
  1471. * @return bool
  1472. * @author Dmitry (dio) Levashov
  1473. **/
  1474. protected function nameAccepted($name) {
  1475. if ($this->nameValidator) {
  1476. if (function_exists($this->nameValidator)) {
  1477. $f = $this->nameValidator;
  1478. return $f($name);
  1479. }
  1480. return preg_match($this->nameValidator, $name);
  1481. }
  1482. return true;
  1483. }
  1484. /**
  1485. * Return new unique name based on file name and suffix
  1486. *
  1487. * @param string $path file path
  1488. * @param string $suffix suffix append to name
  1489. * @return string
  1490. * @author Dmitry (dio) Levashov
  1491. **/
  1492. protected function uniqueName($dir, $name, $suffix = ' copy', $checkNum=true) {
  1493. $ext = '';
  1494. if (!$this->_isDir($this->_joinPath($dir, $name))) {
  1495. if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
  1496. $ext = '.'.$m[1];
  1497. $name = substr($name, 0, strlen($name)-strlen($m[0]));
  1498. }
  1499. }
  1500. if ($checkNum && preg_match('/('.$suffix.')(\d*)$/i', $name, $m)) {
  1501. $i = (int)$m[2];
  1502. $name = substr($name, 0, strlen($name)-strlen($m[2]));
  1503. } else {
  1504. $i = 0;
  1505. $name .= $suffix;
  1506. }
  1507. $max = $i+100000;
  1508. while ($i <= $max) {
  1509. $n = $name.($i > 0 ? $i : '').$ext;
  1510. if (!$this->_fileExists($this->_joinPath($dir, $n))) {
  1511. return $n;
  1512. }
  1513. $i++;
  1514. }
  1515. return $name.md5($dir).$ext;
  1516. }
  1517. /*********************** file stat *********************/
  1518. /**
  1519. * Check file attribute
  1520. *
  1521. * @param string $path file path
  1522. * @param string $name attribute name (read|write|locked|hidden)
  1523. * @return bool
  1524. * @author Dmitry (dio) Levashov
  1525. **/
  1526. protected function attr($path, $name) {
  1527. if (!isset($this->defaults[$name])) {
  1528. return false;
  1529. }
  1530. $defaults = $perm1 = $perm2 = $perm3 = $this->defaults[$name];
  1531. switch ($name) {
  1532. case 'read': $perm1 = $this->_isReadable($path); break;
  1533. case 'write': $perm1 = $this->_isWritable($path); break;
  1534. case 'locked': $perm1 = $this->_isLocked($path); break;
  1535. case 'hidden': $perm1 = $this->_isHidden($path); break;
  1536. }
  1537. $path = $this->separator.$this->_relpath($path);
  1538. if ($this->access) {
  1539. if (is_array($this->access)) {
  1540. $obj = $this->access[0];
  1541. $method = $this->access[1];
  1542. $perm2 = $obj->{$method}($name, $path, $this->options['accessControlData'], $this);
  1543. } else {
  1544. $func = $this->access;
  1545. $perm2 = $func($name, $path, $this->options['accessControlData'], $this);
  1546. }
  1547. }
  1548. for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
  1549. $attrs = $this->attributes[$i];
  1550. if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
  1551. $perm3 = $attrs[$name];
  1552. break;
  1553. }
  1554. }
  1555. $ret = $name == 'read' || $name == 'write'
  1556. ? $defaults & $perm1 & $perm2 & $perm3
  1557. : $defaults ^ $perm1 ^ $perm2 ^ $perm3;
  1558. return $ret;
  1559. }
  1560. /**
  1561. * Return fileinfo
  1562. *
  1563. * @param string $path file cache
  1564. * @return array
  1565. * @author Dmitry (dio) Levashov
  1566. **/
  1567. protected function stat($path) {
  1568. $root = $path == $this->root;
  1569. $link = !$root && $this->_isLink($path);
  1570. if (!$path || (!$this->_fileExists($path) && !$link)) {
  1571. return false;
  1572. }
  1573. $dir = $this->_isDir($path);
  1574. $file = array(
  1575. 'hash' => $this->encode($path),
  1576. 'phash' => $root ? '' : $this->encode(dirname($path)),
  1577. 'name' => $root ? $this->rootName : $this->_basename($path)
  1578. );
  1579. if ($link) {
  1580. $stat = $this->_lstat($path);
  1581. $file['size'] = $stat['size'];
  1582. $file['date'] = $this->formatDate($stat['mtime']);
  1583. } else {
  1584. $file['size'] = $dir ? 0 : $this->_filesize($path);
  1585. $file['date'] = $this->formatDate($this->_filemtime($path));
  1586. }
  1587. if ($link) {
  1588. if (($target = $this->_readlink($path)) != false) {
  1589. $file['mime'] = $this->mimetype($target);
  1590. $file['alias'] = $this->_path($target);
  1591. $file['read'] = (int)$this->attr($path, 'read');
  1592. $file['write'] = (int)$this->attr($path, 'write');
  1593. } else {
  1594. $file['mime'] = 'symlink-broken';
  1595. $file['read'] = 0;
  1596. $file['write'] = 0;
  1597. }
  1598. } else {
  1599. $file['mime'] = $dir ? 'directory' : $this->mimetype($path);
  1600. $file['read'] = (int)$this->attr($path, 'read');
  1601. $file['write'] = (int)$this->attr($path, 'write');
  1602. }
  1603. if ($this->attr($path, 'locked')) {
  1604. $file['locked'] = 1;
  1605. }
  1606. if ($this->attr($path, 'hidden')) {
  1607. $file['hidden'] = 1;
  1608. }
  1609. if ($this->options['utf8fix']) {
  1610. $file['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], json_encode($file['name'])));
  1611. }
  1612. if ($root) {
  1613. $file['volumeid'] = $this->id;
  1614. }
  1615. if ($file['read'] && !isset($file['hidden'])) {
  1616. if ($dir) {
  1617. if (!$link && $this->_subdirs($path)) {
  1618. $file['dirs'] = 1;
  1619. }
  1620. } else {
  1621. if (($tmb = $this->gettmb($path)) != false) {
  1622. $file['tmb'] = $tmb;
  1623. } elseif ($this->canCreateTmb($path, $file['mime'])) {
  1624. $file['tmb'] = 1;
  1625. }
  1626. }
  1627. }
  1628. return $file;
  1629. }
  1630. /**
  1631. * Return file mimetype
  1632. *
  1633. * @param string $path file path
  1634. * @return string
  1635. * @author Dmitry (dio) Levashov
  1636. **/
  1637. protected function mimetype($path) {
  1638. $type = '';
  1639. if ($this->mimeDetect == 'finfo') {
  1640. if (empty($this->finfo)) {
  1641. $this->finfo = finfo_open(FILEINFO_MIME);
  1642. }
  1643. $type = @finfo_file($this->finfo, $path);
  1644. } elseif ($type == 'mime_content_type') {
  1645. $type = mime_content_type($path);
  1646. } else {
  1647. $pinfo = pathinfo($path);
  1648. $ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
  1649. $type = isset(self::$mimetypes[$ext]) ? self::$mimetypes[$ext] : 'unknown';
  1650. }
  1651. $type = explode(';', $type);
  1652. $type = trim($type[0]);
  1653. if ($type == 'unknown') {
  1654. if ($this->_isLink($path)) {
  1655. $target = $this->_readlink($path);
  1656. $type = $target ? $this->mimetype($target) : 'symlink-broken';
  1657. } else if ($this->_isDir($path)) {
  1658. $type = 'directory';
  1659. } elseif ($this->_filesize($path) == 0 || preg_match('/\.(ini|conf)$/i', $path)) {
  1660. $type = 'text/plain';
  1661. }
  1662. } elseif ($type == 'application/x-empty') {
  1663. // finfo return this mime for empty files
  1664. $type = 'text/plain';
  1665. } elseif ($type == 'application/x-zip') {
  1666. // http://elrte.org/redmine/issues/163
  1667. $type = 'application/zip';
  1668. }
  1669. return $type;
  1670. }
  1671. /**
  1672. * Return file/total directory size
  1673. *
  1674. * @param string $path file path
  1675. * @return int
  1676. * @author Dmitry (dio) Levashov
  1677. **/
  1678. protected function countSize($path) {
  1679. if (!$this->_fileExists($path)
  1680. || !$this->attr($path, 'read')
  1681. || $this->attr($path, 'hidden')) {
  1682. return 0;
  1683. }
  1684. if ($this->_isLink($path)) {
  1685. $lstat = $this->_lstat($path);
  1686. return $lstat['size'];
  1687. }
  1688. if ($this->_isFile($path)) {
  1689. return $this->_filesize($path);
  1690. }
  1691. $size = 0;
  1692. foreach ($this->_scandir($path) as $p) {
  1693. $name = $this->_basename($p);
  1694. if ($name != '.' && $p !

Large files files are truncated, but you can click here to view the full file