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

/vendor/elfinder/src/connectors/php/elFinder.class.php

https://bitbucket.org/seyar/parshin.local
PHP | 1861 lines | 1333 code | 150 blank | 378 comment | 454 complexity | 833fabc53047c67732f3a1bc6a632c5a MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1

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

  1. <?php
  2. // set locale to UTF8 to correct multibyte characters
  3. setlocale(LC_ALL, 'en_US.UTF8');
  4. interface elFinderILogger {
  5. public function log($cmd, $ok, $context, $err='', $errorData = array());
  6. }
  7. class elFinder {
  8. /**
  9. * object options
  10. *
  11. * @var array
  12. **/
  13. private $_options = array(
  14. 'root' => '', // path to root directory
  15. 'URL' => '', // root directory URL
  16. 'rootAlias' => 'Home', // display this instead of root directory name
  17. 'disabled' => array(), // list of not allowed commands
  18. 'dotFiles' => false, // display dot files
  19. 'dirSize' => true, // count total directories sizes
  20. 'fileMode' => 0666, // new files mode
  21. 'dirMode' => 0777, // new folders mode
  22. 'mimeDetect' => 'auto', // files mimetypes detection method (finfo, mime_content_type, linux (file -ib), bsd (file -Ib), internal (by extensions))
  23. 'uploadAllow' => array(), // mimetypes which allowed to upload
  24. 'uploadDeny' => array(), // mimetypes which not allowed to upload
  25. 'uploadOrder' => 'deny,allow', // order to proccess uploadAllow and uploadAllow options
  26. 'imgLib' => 'auto', // image manipulation library (imagick, mogrify, gd)
  27. 'tmbDir' => '.tmb', // directory name for image thumbnails. Set to "" to avoid thumbnails generation
  28. 'tmbCleanProb' => 1, // how frequiently clean thumbnails dir (0 - never, 200 - every init request)
  29. 'tmbAtOnce' => 5, // number of thumbnails to generate per request
  30. 'tmbSize' => 48, // images thumbnails size (px)
  31. 'fileURL' => true, // display file URL in "get info"
  32. 'dateFormat' => 'j M Y H:i', // file modification date format
  33. 'logger' => null, // object logger
  34. 'aclObj' => null, // acl object (not implemented yet)
  35. 'aclRole' => 'user', // role for acl
  36. 'defaults' => array( // default permisions
  37. 'read' => true,
  38. 'write' => true,
  39. 'rm' => true
  40. ),
  41. 'perms' => array(), // individual folders/files permisions
  42. 'debug' => false, // send debug to client
  43. 'archiveMimes' => array(), // allowed archive's mimetypes to create. Leave empty for all available types.
  44. 'archivers' => array() // info about archivers to use. See example below. Leave empty for auto detect
  45. // 'archivers' => array(
  46. // 'create' => array(
  47. // 'application/x-gzip' => array(
  48. // 'cmd' => 'tar',
  49. // 'argc' => '-czf',
  50. // 'ext' => 'tar.gz'
  51. // )
  52. // ),
  53. // 'extract' => array(
  54. // 'application/x-gzip' => array(
  55. // 'cmd' => 'tar',
  56. // 'argc' => '-xzf',
  57. // 'ext' => 'tar.gz'
  58. // ),
  59. // 'application/x-bzip2' => array(
  60. // 'cmd' => 'tar',
  61. // 'argc' => '-xjf',
  62. // 'ext' => 'tar.bz'
  63. // )
  64. // )
  65. // )
  66. );
  67. /**
  68. * mapping $_GET['cmd]/$_POST['cmd] to class methods
  69. *
  70. * @var array
  71. **/
  72. private $_commands = array(
  73. 'open' => '_open',
  74. 'reload' => '_reload',
  75. 'mkdir' => '_mkdir',
  76. 'mkfile' => '_mkfile',
  77. 'rename' => '_rename',
  78. 'upload' => '_upload',
  79. 'paste' => '_paste',
  80. 'rm' => '_rm',
  81. 'duplicate' => '_duplicate',
  82. 'read' => '_fread',
  83. 'edit' => '_edit',
  84. 'archive' => '_archive',
  85. 'extract' => '_extract',
  86. 'resize' => '_resize',
  87. 'tmb' => '_thumbnails',
  88. 'ping' => '_ping'
  89. );
  90. /**
  91. * List of commands to log
  92. *
  93. * @var string
  94. **/
  95. public $_loggedCommands = array('mkdir', 'mkfile', 'rename', 'upload', 'paste', 'rm', 'duplicate', 'edit', 'resize');
  96. /**
  97. * Context to log command
  98. *
  99. * @var string
  100. **/
  101. private $_logContext = array();
  102. /**
  103. * extensions/mimetypes for _mimetypeDetect = 'internal'
  104. *
  105. * @var array
  106. **/
  107. private $_mimeTypes = array(
  108. //applications
  109. 'ai' => 'application/postscript',
  110. 'eps' => 'application/postscript',
  111. 'exe' => 'application/octet-stream',
  112. 'doc' => 'application/vnd.ms-word',
  113. 'xls' => 'application/vnd.ms-excel',
  114. 'ppt' => 'application/vnd.ms-powerpoint',
  115. 'pps' => 'application/vnd.ms-powerpoint',
  116. 'pdf' => 'application/pdf',
  117. 'xml' => 'application/xml',
  118. 'odt' => 'application/vnd.oasis.opendocument.text',
  119. 'swf' => 'application/x-shockwave-flash',
  120. // archives
  121. 'gz' => 'application/x-gzip',
  122. 'tgz' => 'application/x-gzip',
  123. 'bz' => 'application/x-bzip2',
  124. 'bz2' => 'application/x-bzip2',
  125. 'tbz' => 'application/x-bzip2',
  126. 'zip' => 'application/zip',
  127. 'rar' => 'application/x-rar',
  128. 'tar' => 'application/x-tar',
  129. '7z' => 'application/x-7z-compressed',
  130. // texts
  131. 'txt' => 'text/plain',
  132. 'php' => 'text/x-php',
  133. 'html' => 'text/html',
  134. 'htm' => 'text/html',
  135. 'js' => 'text/javascript',
  136. 'css' => 'text/css',
  137. 'rtf' => 'text/rtf',
  138. 'rtfd' => 'text/rtfd',
  139. 'py' => 'text/x-python',
  140. 'java' => 'text/x-java-source',
  141. 'rb' => 'text/x-ruby',
  142. 'sh' => 'text/x-shellscript',
  143. 'pl' => 'text/x-perl',
  144. 'sql' => 'text/x-sql',
  145. // images
  146. 'bmp' => 'image/x-ms-bmp',
  147. 'jpg' => 'image/jpeg',
  148. 'jpeg' => 'image/jpeg',
  149. 'gif' => 'image/gif',
  150. 'png' => 'image/png',
  151. 'tif' => 'image/tiff',
  152. 'tiff' => 'image/tiff',
  153. 'tga' => 'image/x-targa',
  154. 'psd' => 'image/vnd.adobe.photoshop',
  155. //audio
  156. 'mp3' => 'audio/mpeg',
  157. 'mid' => 'audio/midi',
  158. 'ogg' => 'audio/ogg',
  159. 'mp4a' => 'audio/mp4',
  160. 'wav' => 'audio/wav',
  161. 'wma' => 'audio/x-ms-wma',
  162. // video
  163. 'avi' => 'video/x-msvideo',
  164. 'dv' => 'video/x-dv',
  165. 'mp4' => 'video/mp4',
  166. 'mpeg' => 'video/mpeg',
  167. 'mpg' => 'video/mpeg',
  168. 'mov' => 'video/quicktime',
  169. 'wm' => 'video/x-ms-wmv',
  170. 'flv' => 'video/x-flv',
  171. 'mkv' => 'video/x-matroska'
  172. );
  173. /**
  174. * undocumented class variable
  175. *
  176. * @var string
  177. **/
  178. private $_time = 0;
  179. /**
  180. * Additional data about error
  181. *
  182. * @var array
  183. **/
  184. private $_errorData = array();
  185. /**
  186. * undocumented class variable
  187. *
  188. * @var string
  189. **/
  190. private $_fakeRoot = '';
  191. /**
  192. * Command result to send to client
  193. *
  194. * @var array
  195. **/
  196. private $_result = array();
  197. /**
  198. * undocumented class variable
  199. *
  200. * @var string
  201. **/
  202. private $_today = 0;
  203. /**
  204. * undocumented class variable
  205. *
  206. * @var string
  207. **/
  208. private $_yesterday = 0;
  209. /**
  210. * constructor
  211. *
  212. * @param array object options
  213. * @return void
  214. **/
  215. public function __construct($options=array()) {
  216. foreach ($this->_options as $k=>$v) {
  217. if (isset($options[$k])) {
  218. $this->_options[$k] = is_array($this->_options[$k])
  219. ? array_merge($this->_options[$k], $options[$k])
  220. : $options[$k];
  221. }
  222. }
  223. if (substr($this->_options['root'], -1) == DIRECTORY_SEPARATOR) {
  224. $this->_options['root'] = substr($this->_options['root'], 0, -1);
  225. }
  226. $this->_time = $this->_options['debug'] ? $this->_utime() : 0;
  227. $this->_fakeRoot = !$this->_options['rootAlias']
  228. ? $this->_options['root']
  229. : dirname($this->_options['root']).DIRECTORY_SEPARATOR.$this->_options['rootAlias'];
  230. if (!empty($this->_options['disabled'])) {
  231. $no = array('open', 'reload', 'tmb', 'ping');
  232. foreach ($this->_options['disabled'] as $k => $c) {
  233. if (!isset($this->_commands[$c]) || in_array($c, $no)) {
  234. unset($this->_options['disabled'][$k]);
  235. } else {
  236. unset($this->_commands[$c]);
  237. }
  238. }
  239. }
  240. if ($this->_options['tmbDir']) {
  241. $tmbDir = $this->_options['root'].DIRECTORY_SEPARATOR.$this->_options['tmbDir'];
  242. $this->_options['tmbDir'] = is_dir($tmbDir) || @mkdir($tmbDir, $this->_options['dirMode']) ? $tmbDir : '';
  243. }
  244. if ($this->_options['tmbDir']) {
  245. if (!in_array($this->_options['imgLib'], array('imagick', 'mogrify', 'gd'))) {
  246. $this->_options['imgLib'] = $this->_getImgLib();
  247. }
  248. }
  249. $this->_today = mktime(0,0,0, date('m'), date('d'), date('Y'));
  250. $this->_yesterday = $this->_today-86400;
  251. }
  252. /**
  253. * Proccess client request and output json
  254. *
  255. * @return void
  256. **/
  257. public function run() {
  258. if (empty($this->_options['root']) || !is_dir($this->_options['root'])) {
  259. exit(json_encode(array('error' => 'Invalid backend configuration')));
  260. }
  261. if (!$this->_isAllowed($this->_options['root'], 'read')) {
  262. exit(json_encode(array('error' => 'Access denied')));
  263. }
  264. $cmd = '';
  265. if (!empty($_POST['cmd'])) {
  266. $cmd = trim($_POST['cmd']);
  267. } elseif (!empty($_GET['cmd'])) {
  268. $cmd = trim($_GET['cmd']);
  269. }
  270. if (!$cmd && $_SERVER["REQUEST_METHOD"] == 'POST') {
  271. header("Content-Type: text/html");
  272. $this->_result['error'] = 'Data exceeds the maximum allowed size';
  273. exit(json_encode($this->_result));
  274. }
  275. if ($cmd && (empty($this->_commands[$cmd]) || !method_exists($this, $this->_commands[$cmd]))) {
  276. exit(json_encode(array('error' => 'Unknown command')));
  277. }
  278. if (isset($_GET['init'])) {
  279. $ts = $this->_utime();
  280. $this->_result['disabled'] = $this->_options['disabled'];
  281. $this->_result['params'] = array(
  282. 'dotFiles' => $this->_options['dotFiles'],
  283. 'uplMaxSize' => ini_get('upload_max_filesize'),
  284. 'archives' => array(),
  285. 'extract' => array(),
  286. 'url' => $this->_options['fileURL'] ? $this->_options['URL'] : ''
  287. );
  288. if (isset($this->_commands['archive']) || isset($this->_commands['extract'])) {
  289. $this->_checkArchivers();
  290. if (isset($this->_commands['archive'])) {
  291. $this->_result['params']['archives'] = $this->_options['archiveMimes'];
  292. }
  293. if (isset($this->_commands['extract'])) {
  294. $this->_result['params']['extract'] = array_keys($this->_options['archivers']['extract']);
  295. }
  296. }
  297. // clean thumbnails dir
  298. if ($this->_options['tmbDir']) {
  299. srand((double) microtime() * 1000000);
  300. if (rand(1, 200) <= $this->_options['tmbCleanProb']) {
  301. $ts2 = $this->_utime();
  302. $ls = scandir($this->_options['tmbDir']);
  303. for ($i=0, $s = count($ls); $i < $s; $i++) {
  304. if ('.' != $ls[$i] && '..' != $ls[$i]) {
  305. @unlink($this->_options['tmbDir'].DIRECTORY_SEPARATOR.$ls[$i]);
  306. }
  307. }
  308. }
  309. }
  310. }
  311. if ($cmd) {
  312. $this->{$this->_commands[$cmd]}();
  313. } else {
  314. $this->_open();
  315. }
  316. if ($this->_options['debug']) {
  317. $this->_result['debug'] = array(
  318. 'time' => $this->_utime() - $this->_time,
  319. 'mimeDetect' => $this->_options['mimeDetect'],
  320. 'imgLib' => $this->_options['imgLib']
  321. );
  322. if ($this->_options['dirSize']) {
  323. $this->_result['debug']['dirSize'] = true;
  324. $this->_result['debug']['du'] = @$this->_options['du'];
  325. }
  326. }
  327. header("Content-Type: ".($cmd == 'upload' ? 'text/html' : 'application/json'));
  328. header("Connection: close");
  329. echo json_encode($this->_result);
  330. if (!empty($this->_options['logger']) && in_array($cmd, $this->_loggedCommands)) {
  331. $this->_options['logger']->log($cmd, empty($this->_result['error']), $this->_logContext, !empty($this->_result['error']) ? $this->_result['error'] : '', !empty($this->_result['errorData']) ? $this->_result['errorData'] : array());
  332. }
  333. exit();
  334. }
  335. /************************************************************/
  336. /** elFinder commands **/
  337. /************************************************************/
  338. /**
  339. * Return current dir content to client or output file content to browser
  340. *
  341. * @return void
  342. **/
  343. private function _open()
  344. {
  345. if (isset($_GET['current'])) { // read file
  346. if (empty($_GET['current'])
  347. || empty($_GET['target'])
  348. || false == ($dir = $this->_findDir(trim($_GET['current'])))
  349. || false == ($file = $this->_find(trim($_GET['target']), $dir))
  350. || is_dir($file)
  351. ) {
  352. header('HTTP/1.x 404 Not Found');
  353. exit('File not found');
  354. }
  355. if (!$this->_isAllowed($dir, 'read') || !$this->_isAllowed($file, 'read')) {
  356. header('HTTP/1.x 403 Access Denied');
  357. exit('Access denied');
  358. }
  359. if (filetype($file) == 'link') {
  360. $file = $this->_readlink($file);
  361. if (!$file || is_dir($file)) {
  362. header('HTTP/1.x 404 Not Found');
  363. exit('File not found');
  364. }
  365. if (!$this->_isAllowed(dirname($file), 'read') || !$this->_isAllowed($file, 'read')) {
  366. header('HTTP/1.x 403 Access Denied');
  367. exit('Access denied');
  368. }
  369. }
  370. $mime = $this->_mimetype($file);
  371. $parts = explode('/', $mime);
  372. $disp = $parts[0] == 'image' || $parts[0] == 'text' ? 'inline' : 'attachments';
  373. header("Content-Type: ".$mime);
  374. header("Content-Disposition: ".$disp."; filename=".basename($file));
  375. header("Content-Location: ".str_replace($this->_options['root'], '', $file));
  376. header('Content-Transfer-Encoding: binary');
  377. header("Content-Length: ".filesize($file));
  378. header("Connection: close");
  379. readfile($file);
  380. exit();
  381. } else { // enter directory
  382. $path = $this->_options['root'];
  383. if (!empty($_GET['target'])) {
  384. if (false == ($p = $this->_findDir(trim($_GET['target'])))) {
  385. if (!isset($_GET['init'])) {
  386. $this->_result['error'] = 'Invalid parameters';
  387. }
  388. } elseif (!$this->_isAllowed($p, 'read')) {
  389. if (!isset($_GET['init'])) {
  390. $this->_result['error'] = 'Access denied';
  391. }
  392. } else {
  393. $path = $p;
  394. }
  395. }
  396. $this->_content($path, isset($_GET['tree']));
  397. }
  398. }
  399. /**
  400. * Rename file/folder
  401. *
  402. * @return void
  403. **/
  404. private function _rename()
  405. {
  406. if (empty($_GET['current'])
  407. || empty($_GET['target'])
  408. || false == ($dir = $this->_findDir(trim($_GET['current'])))
  409. || false == ($target = $this->_find(trim($_GET['target']), $dir))
  410. ) {
  411. $this->_result['error'] = 'File not found';
  412. } elseif (false == ($name = $this->_checkName($_GET['name'])) ) {
  413. $this->_result['error'] = 'Invalid name';
  414. } elseif (!$this->_isAllowed($dir, 'write')) {
  415. $this->_result['error'] = 'Access denied';
  416. } elseif (file_exists($dir.DIRECTORY_SEPARATOR.$name)) {
  417. $this->_result['error'] = 'File or folder with the same name already exists';
  418. } elseif (!rename($target, $dir.DIRECTORY_SEPARATOR.$name)) {
  419. $this->_result['error'] = 'Unable to rename file';
  420. } else {
  421. $this->_rmTmb($target);
  422. $this->_logContext['from'] = $target;
  423. $this->_logContext['to'] = $dir.DIRECTORY_SEPARATOR.$name;
  424. $this->_result['select'] = array($this->_hash($dir.DIRECTORY_SEPARATOR.$name));
  425. $this->_content($dir, is_dir($dir.DIRECTORY_SEPARATOR.$name));
  426. }
  427. }
  428. /**
  429. * Create new folder
  430. *
  431. * @return void
  432. **/
  433. private function _mkdir()
  434. {
  435. if (empty($_GET['current']) || false == ($dir = $this->_findDir(trim($_GET['current'])))) {
  436. return $this->_result['error'] = 'Invalid parameters';
  437. }
  438. $this->_logContext['dir'] = $dir.DIRECTORY_SEPARATOR.$_GET['name'];
  439. if (!$this->_isAllowed($dir, 'write')) {
  440. $this->_result['error'] = 'Access denied';
  441. } elseif (false == ($name = $this->_checkName($_GET['name'])) ) {
  442. $this->_result['error'] = 'Invalid name';
  443. } elseif (file_exists($dir.DIRECTORY_SEPARATOR.$name)) {
  444. $this->_result['error'] = 'File or folder with the same name already exists';
  445. } elseif (!@mkdir($dir.DIRECTORY_SEPARATOR.$name, $this->_options['dirMode'])) {
  446. $this->_result['error'] = 'Unable to create folder';
  447. } else {
  448. $this->_logContext['dir'] = $dir.DIRECTORY_SEPARATOR.$name;
  449. $this->_result['select'] = array($this->_hash($dir.DIRECTORY_SEPARATOR.$name));
  450. $this->_content($dir, true);
  451. }
  452. }
  453. /**
  454. * Create new empty file
  455. *
  456. * @return void
  457. **/
  458. private function _mkfile()
  459. {
  460. if (empty($_GET['current'])
  461. || false == ($dir = $this->_findDir(trim($_GET['current'])))) {
  462. return $this->_result['error'] = 'Invalid parameters';
  463. }
  464. $this->_logContext['file'] = $dir.DIRECTORY_SEPARATOR.$_GET['name'];
  465. if (!$this->_isAllowed($dir, 'write')) {
  466. $this->_result['error'] = 'Access denied';
  467. } elseif (false == ($name = $this->_checkName($_GET['name'])) ) {
  468. $this->_result['error'] = 'Invalid name';
  469. } elseif (file_exists($dir.DIRECTORY_SEPARATOR.$name)) {
  470. $this->_result['error'] = 'File or folder with the same name already exists';
  471. } else {
  472. $f = $dir.DIRECTORY_SEPARATOR.$name;
  473. $this->_logContext['file'] = $f;
  474. if (false != ($fp = @fopen($f, 'wb'))) {
  475. fwrite($fp, "");
  476. fclose($fp);
  477. $this->_result['select'] = array($this->_hash($dir.DIRECTORY_SEPARATOR.$name));
  478. $this->_content($dir);
  479. } else {
  480. $this->_result['error'] = 'Unable to create file';
  481. }
  482. }
  483. }
  484. /**
  485. * Remove files/folders
  486. *
  487. * @return void
  488. **/
  489. private function _rm()
  490. {
  491. if (empty($_GET['current'])
  492. || false == ($dir = $this->_findDir(trim($_GET['current'])))
  493. || (empty($_GET['targets']) || !is_array($_GET['targets']))) {
  494. return $this->_result['error'] = 'Invalid parameters';
  495. }
  496. $this->_logContext['targets'] = array();
  497. foreach ($_GET['targets'] as $hash) {
  498. if (false != ($f = $this->_find($hash, $dir))) {
  499. $this->_remove($f);
  500. $this->_logContext['targets'][] = $f;
  501. }
  502. }
  503. if (!empty($this->_result['errorData'])) {
  504. $this->_result['error'] = 'Unable to remove file';
  505. }
  506. $this->_content($dir, true);
  507. }
  508. /**
  509. * Upload files
  510. *
  511. * @return void
  512. **/
  513. private function _upload()
  514. {
  515. if (empty($_POST['current'])
  516. || false == ($dir = $this->_findDir(trim($_POST['current'])))) {
  517. return $this->_result['error'] = 'Invalid parameters';
  518. }
  519. if (!$this->_isAllowed($dir, 'write')) {
  520. return $this->_result['error'] = 'Access denied';
  521. }
  522. if (empty($_FILES['upload']))
  523. {
  524. return $this->_result['error'] = 'No file to upload';
  525. }
  526. $this->_logContext['upload'] = array();
  527. $this->_result['select'] = array();
  528. $total = 0;
  529. for ($i=0, $s = count($_FILES['upload']['name']); $i < $s; $i++) {
  530. if (!empty($_FILES['upload']['name'][$i])) {
  531. $total++;
  532. $this->_logContext['upload'][] = $_FILES['upload']['name'][$i];
  533. if ($_FILES['upload']['error'][$i] > 0) {
  534. $error = 'Unable to upload file';
  535. switch ($_FILES['upload']['error'][$i]) {
  536. case UPLOAD_ERR_INI_SIZE:
  537. case UPLOAD_ERR_FORM_SIZE:
  538. $error = 'File exceeds the maximum allowed filesize';
  539. break;
  540. case UPLOAD_ERR_EXTENSION:
  541. $error = 'Not allowed file type';
  542. break;
  543. }
  544. $this->_errorData($_FILES['upload']['name'][$i], $error);
  545. } elseif (false == ($name = $this->_checkName($_FILES['upload']['name'][$i]))) {
  546. $this->_errorData($_FILES['upload']['name'][$i], 'Invalid name');
  547. } elseif (!$this->_isUploadAllow($_FILES['upload']['name'][$i], $_FILES['upload']['tmp_name'][$i])) {
  548. $this->_errorData($_FILES['upload']['name'][$i], 'Not allowed file type');
  549. } else {
  550. $file = $dir.DIRECTORY_SEPARATOR.$_FILES['upload']['name'][$i];
  551. if (!@move_uploaded_file($_FILES['upload']['tmp_name'][$i], $file)) {
  552. $this->_errorData($_FILES['upload']['name'][$i], 'Unable to save uploaded file');
  553. } else {
  554. @chmod($file, $this->_options['fileMode']);
  555. $this->_result['select'][] = $this->_hash($file);
  556. }
  557. }
  558. }
  559. }
  560. $errCnt = !empty($this->_result['errorData']) ? count($this->_result['errorData']) : 0;
  561. if ($errCnt == $total) {
  562. $this->_result['error'] = 'Unable to upload files';
  563. } else {
  564. if ($errCnt>0) {
  565. $this->_result['error'] = 'Some files was not uploaded';
  566. }
  567. $this->_content($dir);
  568. }
  569. }
  570. /**
  571. * Copy/move files/folders
  572. *
  573. * @return void
  574. **/
  575. private function _paste()
  576. {
  577. if (empty($_GET['current'])
  578. || false == ($current = $this->_findDir(trim($_GET['current'])))
  579. || empty($_GET['src'])
  580. || false == ($src = $this->_findDir(trim($_GET['src'])))
  581. || empty($_GET['dst'])
  582. || false == ($dst = $this->_findDir(trim($_GET['dst'])))
  583. || empty($_GET['targets']) || !is_array($_GET['targets'])
  584. ) {
  585. return $this->_result['error'] = 'Invalid parameters';
  586. }
  587. $cut = !empty($_GET['cut']);
  588. $this->_logContext['src'] = array();
  589. $this->_logContext['dest'] = $dst;
  590. $this->_logContext['cut'] = $cut;
  591. if (!$this->_isAllowed($dst, 'write') || !$this->_isAllowed($src, 'read')) {
  592. return $this->_result['error'] = 'Access denied';
  593. }
  594. foreach ($_GET['targets'] as $hash) {
  595. if (false == ($f = $this->_find($hash, $src))) {
  596. return $this->_result['error'] = 'File not found' && $this->_content($current, true);
  597. }
  598. $this->_logContext['src'][] = $f;
  599. $_dst = $dst.DIRECTORY_SEPARATOR.basename($f);
  600. if (0 === strpos($dst, $f)) {
  601. return $this->_result['error'] = 'Unable to copy into itself' && $this->_content($current, true);
  602. } elseif (file_exists($_dst)) {
  603. return $this->_result['error'] = 'File or folder with the same name already exists' && $this->_content($current, true);
  604. } elseif ($cut && !$this->_isAllowed($f, 'rm')) {
  605. return $this->_result['error'] = 'Access denied' && $this->_content($current, true);
  606. }
  607. if ($cut) {
  608. if (!@rename($f, $_dst)) {
  609. return $this->_result['error'] = 'Unable to move files' && $this->_content($current, true);
  610. } elseif (!is_dir($f)) {
  611. $this->_rmTmb($f);
  612. }
  613. } elseif (!$this->_copy($f, $_dst)) {
  614. return $this->_result['error'] = 'Unable to copy files' && $this->_content($current, true);
  615. }
  616. }
  617. $this->_content($current, true);
  618. }
  619. /**
  620. * Create file/folder copy with suffix - "copy"
  621. *
  622. * @return void
  623. **/
  624. private function _duplicate()
  625. {
  626. if (empty($_GET['current'])
  627. || false == ($current = $this->_findDir(trim($_GET['current'])))
  628. || empty($_GET['target'])
  629. || false == ($target = $this->_find(trim($_GET['target']), $current))
  630. ) {
  631. return $this->_result['error'] = 'Invalid parameters';
  632. }
  633. $this->_logContext['target'] = $target;
  634. if (!$this->_isAllowed($current, 'write') || !$this->_isAllowed($target, 'read')) {
  635. return $this->_result['error'] = 'Access denied';
  636. }
  637. $dup = $this->_uniqueName($target);
  638. if (!$this->_copy($target, $dup)) {
  639. return $this->_result['error'] = 'Unable to create file copy';
  640. }
  641. $this->_result['select'] = array($this->_hash($dup));
  642. $this->_content($current, is_dir($target));
  643. }
  644. /**
  645. * Resize image
  646. *
  647. * @return void
  648. **/
  649. private function _resize()
  650. {
  651. if (empty($_GET['current'])
  652. || false == ($current = $this->_findDir(trim($_GET['current'])))
  653. || empty($_GET['target'])
  654. || false == ($target = $this->_find(trim($_GET['target']), $current))
  655. || empty($_GET['width']) || 0 >= ($width = intval($_GET['width']))
  656. || empty($_GET['height']) || 0 >= ($height = intval($_GET['height']))
  657. ) {
  658. return $this->_result['error'] = 'Invalid parameters';
  659. }
  660. $this->_logContext = array(
  661. 'target' => $target,
  662. 'width' => $width,
  663. 'height' => $height
  664. );
  665. if (!$this->_isAllowed($target, 'write')) {
  666. return $this->_result['error'] = 'Access denied';
  667. }
  668. if (0 !== strpos($this->_mimetype($target), 'image')) {
  669. return $this->_result['error'] = 'File is not an image';
  670. }
  671. if (!$this->_resizeImg($target, $width, $height)) {
  672. return $this->_result['error'] = 'Unable to resize image';
  673. }
  674. $this->_result['select'] = array($this->_hash($target));
  675. $this->_content($current);
  676. }
  677. /**
  678. * Create images thumbnails
  679. *
  680. * @return void
  681. **/
  682. private function _thumbnails()
  683. {
  684. if (!empty($this->_options['tmbDir']) && !empty($_GET['current']) && false != ($current = $this->_findDir(trim($_GET['current'])))) {
  685. $this->_result['current'] = $this->_hash($current);
  686. $this->_result['images'] = array();
  687. $ls = scandir($current);
  688. $cnt = 0;
  689. $max = $this->_options['tmbAtOnce'] > 0 ? intval($this->_options['tmbAtOnce']) : 5;
  690. for ($i=0; $i < count($ls); $i++) {
  691. if ($this->_isAccepted($ls[$i])) {
  692. $path = $current.DIRECTORY_SEPARATOR.$ls[$i];
  693. if (is_readable($path) && $this->_canCreateTmb($this->_mimetype($path))) {
  694. $tmb = $this->_tmbPath($path);
  695. if (!file_exists($tmb)) {
  696. if ($cnt>=$max) {
  697. return $this->_result['tmb'] = true;
  698. } elseif ($this->_tmb($path, $tmb)) {
  699. $this->_result['images'][$this->_hash($path)] = $this->_path2url($tmb);
  700. $cnt++;
  701. }
  702. }
  703. }
  704. }
  705. }
  706. }
  707. }
  708. /**
  709. * Return file content to client
  710. *
  711. * @return void
  712. **/
  713. private function _fread()
  714. {
  715. if (empty($_GET['current'])
  716. || false == ($current = $this->_findDir(trim($_GET['current'])))
  717. || empty($_GET['target'])
  718. || false == ($target = $this->_find(trim($_GET['target']), $current))
  719. ) {
  720. return $this->_result['error'] = 'Invalid parameters';
  721. }
  722. if (!$this->_isAllowed($target, 'read')) {
  723. return $this->_result['error'] = 'Access denied';
  724. }
  725. $this->_result['content'] = @file_get_contents($target);
  726. }
  727. /**
  728. * Save data into text file.
  729. *
  730. * @return void
  731. **/
  732. private function _edit()
  733. {
  734. if (empty($_POST['current'])
  735. || false == ($current = $this->_findDir(trim($_POST['current'])))
  736. || empty($_POST['target'])
  737. || false == ($target = $this->_find(trim($_POST['target']), $current))
  738. || !isset($_POST['content'])
  739. ) {
  740. return $this->_result['error'] = 'Invalid parameters';
  741. }
  742. $this->_logContext['target'] = $target;
  743. if (!$this->_isAllowed($target, 'write')) {
  744. return $this->_result['error'] = 'Access denied';
  745. }
  746. if (false === file_put_contents($target, trim($_POST['content']))) {
  747. return $this->_result['error'] = 'Unable to write to file';
  748. }
  749. $this->_result['target'] = $this->_info($target);
  750. // $this->_result['select'] = array($this->_hash($target));
  751. }
  752. /**
  753. * Create archive of selected type
  754. *
  755. * @return void
  756. **/
  757. private function _archive()
  758. {
  759. $this->_checkArchivers();
  760. if (empty($this->_options['archivers']['create'])
  761. || empty($_GET['type'])
  762. || empty($this->_options['archivers']['create'][$_GET['type']])
  763. || !in_array($_GET['type'], $this->_options['archiveMimes'])) {
  764. return $this->_result['error'] = 'Invalid parameters';
  765. }
  766. if (empty($_GET['current'])
  767. || empty($_GET['targets'])
  768. || !is_array($_GET['targets'])
  769. || false == ($dir = $this->_findDir(trim($_GET['current'])))
  770. || !$this->_isAllowed($dir, 'write')
  771. ) {
  772. return $this->_result['error'] = 'Invalid parameters';
  773. }
  774. $files = array();
  775. $argc = '';
  776. foreach ($_GET['targets'] as $hash) {
  777. if (false == ($f = $this->_find($hash, $dir))) {
  778. return $this->_result['error'] = 'File not found';
  779. }
  780. $files[] = $f;
  781. $argc .= escapeshellarg(basename($f)).' ';
  782. }
  783. $arc = $this->_options['archivers']['create'][$_GET['type']];
  784. $name = count($files) == 1 ? basename($files[0]) : $_GET['name'];
  785. $name = basename($this->_uniqueName($name.'.'.$arc['ext'], ''));
  786. $cwd = getcwd();
  787. chdir($dir);
  788. $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.$argc;
  789. exec($cmd, $o, $c);
  790. chdir($cwd);
  791. if (file_exists($dir.DIRECTORY_SEPARATOR.$name)) {
  792. $this->_content($dir);
  793. $this->_result['select'] = array($this->_hash($dir.DIRECTORY_SEPARATOR.$name));
  794. } else {
  795. $this->_result['error'] = 'Unable to create archive';
  796. }
  797. }
  798. /**
  799. * Extract files from archive
  800. *
  801. * @return void
  802. **/
  803. private function _extract()
  804. {
  805. if (empty($_GET['current'])
  806. || false == ($current = $this->_findDir(trim($_GET['current'])))
  807. || empty($_GET['target'])
  808. || false == ($file = $this->_find(trim($_GET['target']), $current))
  809. || !$this->_isAllowed($current, 'write')
  810. ) {
  811. return $this->_result['error'] = 'Invalid parameters';
  812. }
  813. $this->_checkArchivers();
  814. $mime = $this->_mimetype($file);
  815. if (empty($this->_options['archivers']['extract'][$mime])) {
  816. return $this->_result['error'] = 'Invalid parameters';
  817. }
  818. $cwd = getcwd();
  819. $arc = $this->_options['archivers']['extract'][$mime];
  820. $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($file));
  821. chdir(dirname($file));
  822. exec($cmd, $o, $c);
  823. chdir($cwd);
  824. if ($c == 0) {
  825. $this->_content($current, true);
  826. } else {
  827. $this->_result['error'] = 'Unable to extract files from archive';
  828. }
  829. }
  830. /**
  831. * Send header Connection: close. Required by safari to fix bug http://www.webmasterworld.com/macintosh_webmaster/3300569.htm
  832. *
  833. * @return void
  834. **/
  835. private function _ping()
  836. {
  837. exit(header("Connection: close"));
  838. }
  839. /************************************************************/
  840. /** "content" methods **/
  841. /************************************************************/
  842. /**
  843. * Set current dir info, content and [dirs tree]
  844. *
  845. * @param string $path current dir path
  846. * @param bool $tree set dirs tree?
  847. * @return void
  848. **/
  849. private function _content($path, $tree=false)
  850. {
  851. $this->_cwd($path);
  852. $this->_cdc($path);
  853. if ($tree) {
  854. $this->_result['tree'] = $this->_tree($this->_options['root']);
  855. }
  856. }
  857. /**
  858. * Set current dir info
  859. *
  860. * @param string $path current dir path
  861. * @return void
  862. **/
  863. private function _cwd($path)
  864. {
  865. $rel = $this->_options['rootAlias'] ? $this->_options['rootAlias'] : basename($this->_options['root']);
  866. if ($path == $this->_options['root']) {
  867. $name = $rel;
  868. } else {
  869. $name = basename($path);
  870. $rel .= DIRECTORY_SEPARATOR.substr($path, strlen($this->_options['root'])+1);
  871. }
  872. $this->_result['cwd'] = array(
  873. 'hash' => $this->_hash($path),
  874. 'name' => $name,
  875. 'mime' => 'directory',
  876. 'rel' => $rel,
  877. 'size' => 0,
  878. 'date' => date($this->_options['dateFormat'], filemtime($path)),
  879. 'read' => true,
  880. 'write' => $this->_isAllowed($path, 'write'),
  881. 'rm' => $path == $this->_options['root'] ? false : $this->_isAllowed($path, 'rm')
  882. );
  883. }
  884. /**
  885. * Set current dir content
  886. *
  887. * @param string $path current dir path
  888. * @return void
  889. **/
  890. private function _cdc($path)
  891. {
  892. $dirs = $files = array();
  893. $ls = scandir($path);
  894. for ($i=0; $i < count($ls); $i++) {
  895. if ($this->_isAccepted($ls[$i])) {
  896. $info = $this->_info($path.DIRECTORY_SEPARATOR.$ls[$i]);
  897. if ($info['mime'] == 'directory') {
  898. $dirs[] = $info;
  899. } else {
  900. $files[] = $info;
  901. }
  902. }
  903. }
  904. $this->_result['cdc'] = array_merge($dirs, $files);
  905. }
  906. /**
  907. * Return file/folder info
  908. *
  909. * @param string $path file path
  910. * @return array
  911. **/
  912. private function _info($path)
  913. {
  914. $type = filetype($path);
  915. $stat = $type == 'link' ? lstat($path) : stat($path);
  916. if ($stat['mtime'] > $this->_today) {
  917. $d = 'Today '.date('H:i', $stat['mtime']);
  918. } elseif ($stat['mtime'] > $this->_yesterday) {
  919. $d = 'Yesterday '.date('H:i', $stat['mtime']);
  920. } else {
  921. $d = date($this->_options['dateFormat'], $stat['mtime']);
  922. }
  923. $info = array(
  924. 'name' => htmlspecialchars(basename($path)),
  925. 'hash' => $this->_hash($path),
  926. 'mime' => $type == 'dir' ? 'directory' : $this->_mimetype($path),
  927. 'date' => $d,
  928. 'size' => $type == 'dir' ? $this->_dirSize($path) : $stat['size'],
  929. 'read' => $this->_isAllowed($path, 'read'),
  930. 'write' => $this->_isAllowed($path, 'write'),
  931. 'rm' => $this->_isAllowed($path, 'rm'),
  932. );
  933. if ($type == 'link') {
  934. if (false == ($lpath = $this->_readlink($path))) {
  935. $info['mime'] = 'symlink-broken';
  936. return $info;
  937. }
  938. if (is_dir($lpath)) {
  939. $info['mime'] = 'directory';
  940. } else {
  941. $info['parent'] = $this->_hash(dirname($lpath));
  942. $info['mime'] = $this->_mimetype($lpath);
  943. }
  944. $info['link'] = $this->_hash($lpath);
  945. $info['linkTo'] = ($this->_options['rootAlias'] ? $this->_options['rootAlias'] : basename($this->_options['root'])).substr($lpath, strlen($this->_options['root']));
  946. $info['read'] = $this->_isAllowed($lpath, 'read');
  947. $info['write'] = $this->_isAllowed($lpath, 'write');
  948. $info['rm'] = $this->_isAllowed($lpath, 'rm');
  949. } else {
  950. $lpath = '';
  951. }
  952. if ($info['mime'] != 'directory') {
  953. if ($this->_options['fileURL'] && $info['read']) {
  954. $info['url'] = $this->_path2url($lpath ? $lpath : $path);
  955. }
  956. if (0 === ($p = strpos($info['mime'], 'image'))) {
  957. if (false != ($s = getimagesize($path))) {
  958. $info['dim'] = $s[0].'x'.$s[1];
  959. }
  960. if ($info['read']) {
  961. $info['resize'] = isset($info['dim']) && $this->_canCreateTmb($info['mime']);
  962. $tmb = $this->_tmbPath($path);
  963. if (file_exists($tmb)) {
  964. $info['tmb'] = $this->_path2url($tmb);
  965. } elseif ($info['resize']) {
  966. $this->_result['tmb'] = true;
  967. }
  968. }
  969. }
  970. }
  971. return $info;
  972. }
  973. /**
  974. * Return directory tree (multidimensional array)
  975. *
  976. * @param string $path directory path
  977. * @return array
  978. **/
  979. private function _tree($path)
  980. {
  981. $dir = array(
  982. 'hash' => $this->_hash($path),
  983. 'name' => $path == $this->_options['root'] && $this->_options['rootAlias'] ? $this->_options['rootAlias'] : basename($path),
  984. 'read' => $this->_isAllowed($path, 'read'),
  985. 'write' => $this->_isAllowed($path, 'write'),
  986. 'dirs' => array()
  987. );
  988. if ($dir['read'] && false != ($ls = scandir($path))) {
  989. for ($i=0; $i < count($ls); $i++) {
  990. $p = $path.DIRECTORY_SEPARATOR.$ls[$i];
  991. if ($this->_isAccepted($ls[$i]) && filetype($p) == 'dir') {
  992. $dir['dirs'][] = $this->_tree($p);
  993. }
  994. }
  995. }
  996. return $dir;
  997. }
  998. /************************************************************/
  999. /** fs methods **/
  1000. /************************************************************/
  1001. /**
  1002. * Return name for duplicated file/folder or new archive
  1003. *
  1004. * @param string $f file/folder name
  1005. * @param string $suffix file name suffix
  1006. * @return string
  1007. **/
  1008. private function _uniqueName($f, $suffix=' copy')
  1009. {
  1010. $dir = dirname($f);
  1011. $name = basename($f);
  1012. $ext = '';
  1013. if (!is_dir($f)) {
  1014. if (preg_match('/\.(tar\.gz|tar\.bz|tar\.bz2|[a-z0-9]{1,4})$/i', $name, $m)) {
  1015. $ext = '.'.$m[1];
  1016. $name = substr($name, 0, strlen($name)-strlen($m[0]));
  1017. }
  1018. }
  1019. if (preg_match('/('.$suffix.')(\d*)$/i', $name, $m)) {
  1020. $i = (int)$m[2];
  1021. $name = substr($name, 0, strlen($name)-strlen($m[2]));
  1022. } else {
  1023. $name .= $suffix;
  1024. $i = 0;
  1025. $n = $dir.DIRECTORY_SEPARATOR.$name.$ext;
  1026. if (!file_exists($n)) {
  1027. return $n;
  1028. }
  1029. }
  1030. while ($i++ <= 10000) {
  1031. $n = $dir.DIRECTORY_SEPARATOR.$name.$i.$ext;
  1032. if (!file_exists($n)) {
  1033. return $n;
  1034. }
  1035. }
  1036. return $dir.DIRECTORY_SEPARATOR.$name.md5($f).$ext;
  1037. }
  1038. /**
  1039. * Remove file or folder (recursively)
  1040. *
  1041. * @param string $path fole/folder path
  1042. * @return void
  1043. **/
  1044. private function _remove($path)
  1045. {
  1046. if (!$this->_isAllowed($path, 'rm')) {
  1047. return $this->_errorData($path, 'Access denied');
  1048. }
  1049. if (!is_dir($path)) {
  1050. if (!@unlink($path)) {
  1051. $this->_errorData($path, 'Unable to remove file');
  1052. } else {
  1053. $this->_rmTmb($path);
  1054. }
  1055. } else {
  1056. $ls = scandir($path);
  1057. for ($i=0; $i < count($ls); $i++) {
  1058. if ('.' != $ls[$i] && '..' != $ls[$i]) {
  1059. $this->_remove($path.DIRECTORY_SEPARATOR.$ls[$i]);
  1060. }
  1061. }
  1062. if (!@rmdir($path)) {
  1063. return $this->_errorData($path, 'Unable to remove file');
  1064. }
  1065. }
  1066. return true;
  1067. }
  1068. /**
  1069. * Copy file/folder (recursively)
  1070. *
  1071. * @param string $src file/folder to copy
  1072. * @param string $trg destination name
  1073. * @return bool
  1074. **/
  1075. private function _copy($src, $trg)
  1076. {
  1077. if (!$this->_isAllowed($src, 'read')) {
  1078. return $this->_errorData($src, 'Access denied');
  1079. }
  1080. $dir = dirname($trg);
  1081. if (!$this->_isAllowed($dir, 'write')) {
  1082. return $this->_errorData($dir, 'Access denied');
  1083. }
  1084. if (file_exists($trg)) {
  1085. return $this->_errorData($src, 'File or folder with the same name already exists');
  1086. }
  1087. if (!is_dir($src)) {
  1088. if (!@copy($src, $trg)) {
  1089. return $this->_errorData($src, 'Unable to copy files');
  1090. }
  1091. @chmod($trg, $this->_options['fileMode']);
  1092. } else {
  1093. if (!@mkdir($trg, $this->_options['dirMode'])) {
  1094. return $this->_errorData($src, 'Unable to copy files');
  1095. }
  1096. $ls = scandir($src);
  1097. for ($i=0; $i < count($ls); $i++) {
  1098. if ('.' != $ls[$i] && '..' != $ls[$i]) {
  1099. $_src = $src.DIRECTORY_SEPARATOR.$ls[$i];
  1100. $_trg = $trg.DIRECTORY_SEPARATOR.$ls[$i];
  1101. if (is_dir($_src)) {
  1102. if (!$this->_copy($_src, $_trg)) {
  1103. return $this->_errorData($_src, 'Unable to copy files');
  1104. }
  1105. } else {
  1106. if (!@copy($_src, $_trg)) {
  1107. return $this->_errorData($_src, 'Unable to copy files');
  1108. }
  1109. @chmod($_trg, $this->_options['fileMode']);
  1110. }
  1111. }
  1112. }
  1113. }
  1114. return true;
  1115. }
  1116. /**
  1117. * Check new file name for invalid simbols. Return name if valid
  1118. *
  1119. * @return string $n file name
  1120. * @return string
  1121. **/
  1122. private function _checkName($n)
  1123. {
  1124. $n = strip_tags(trim($n));
  1125. if (!$this->_options['dotFiles'] && '.' == substr($n, 0, 1)) {
  1126. return false;
  1127. }
  1128. return preg_match('|^[^\\/\<\>:]+$|', $n) ? $n : false;
  1129. }
  1130. /**
  1131. * Find folder by hash in required folder and subfolders
  1132. *
  1133. * @param string $hash folder hash
  1134. * @param string $path folder path to search in
  1135. * @return string
  1136. **/
  1137. private function _findDir($hash, $path='')
  1138. {
  1139. if (!$path) {
  1140. $path = $this->_options['root'];
  1141. if ($this->_hash($path) == $hash) {
  1142. return $path;
  1143. }
  1144. }
  1145. if (false != ($ls = scandir($path))) {
  1146. for ($i=0; $i < count($ls); $i++) {
  1147. $p = $path.DIRECTORY_SEPARATOR.$ls[$i];
  1148. if ($this->_isAccepted($ls[$i]) && is_dir($p)) {
  1149. if ($this->_hash($p) == $hash || false != ($p = $this->_findDir($hash, $p))) {
  1150. return $p;
  1151. }
  1152. }
  1153. }
  1154. }
  1155. }
  1156. /**
  1157. * Find file/folder by hash in required folder
  1158. *
  1159. * @param string $hash file/folder hash
  1160. * @param string $path folder path to search in
  1161. **/
  1162. private function _find($hash, $path)
  1163. {
  1164. if (false != ($ls = scandir($path))) {
  1165. for ($i=0; $i < count($ls); $i++) {
  1166. if ($this->_isAccepted($ls[$i])) {
  1167. $p = $path.DIRECTORY_SEPARATOR.$ls[$i];
  1168. if ($this->_hash($p) == $hash) {
  1169. return $p;
  1170. }
  1171. }
  1172. }
  1173. }
  1174. }
  1175. /**
  1176. * Return path of file on which link point to, if exists in root directory
  1177. *
  1178. * @param string $path symlink path
  1179. * @return string
  1180. **/
  1181. private function _readlink($path)
  1182. {
  1183. $target = readlink($path);
  1184. if ('/' != substr($target, 0, 1)) {
  1185. $target = dirname($path).DIRECTORY_SEPARATOR.$target;
  1186. }
  1187. $target = realpath($target);
  1188. $root = realpath($this->_options['root']);
  1189. return $target && file_exists($target) && 0 === strpos($target, $root) ? $target : false;
  1190. }
  1191. /**
  1192. * Count total directory size if this allowed in options
  1193. *
  1194. * @param string $path directory path
  1195. * @return int
  1196. **/
  1197. private function _dirSize($path)
  1198. {
  1199. $size = 0;
  1200. if (!$this->_options['dirSize'] || !$this->_isAllowed($path, 'read')) {
  1201. return filesize($path);
  1202. }
  1203. if (!isset($this->_options['du'])) {
  1204. $this->_options['du'] = function_exists('exec')
  1205. ? exec('du -h '.escapeshellarg(__FILE__), $o, $s) > 0 && $s == 0
  1206. : false;
  1207. }
  1208. if ($this->_options['du']) {
  1209. $size = intval(exec('du -k '.escapeshellarg($path)))*1024;
  1210. } else {
  1211. $ls = scandir($path);
  1212. for ($i=0; $i < count($ls); $i++) {
  1213. if ($this->_isAccepted($ls[$i])) {
  1214. $p = $path.DIRECTORY_SEPARATOR.$ls[$i];
  1215. $size += filetype($p) == 'dir' && $this->_isAllowed($p, 'read') ? $this->_dirSize($p) : filesize($p);
  1216. }
  1217. }
  1218. }
  1219. return $size;
  1220. }
  1221. /**
  1222. * Return file mimetype
  1223. *
  1224. * @param string $path file path
  1225. * @return string
  1226. **/
  1227. private function _mimetype($path)
  1228. {
  1229. if (empty($this->_options['mimeDetect']) || $this->_options['mimeDetect'] == 'auto') {
  1230. $this->_options['mimeDetect'] = $this->_getMimeDetect();
  1231. }
  1232. switch ($this->_options['mimeDetect']) {
  1233. case 'finfo':
  1234. if (empty($this->_finfo)) {
  1235. $this->_finfo = finfo_open(FILEINFO_MIME);
  1236. }
  1237. $type = @finfo_file($this->_finfo, $path);
  1238. break;
  1239. case 'php':
  1240. $type = mime_content_type($path);
  1241. break;
  1242. case 'linux':
  1243. $type = exec('file -ib '.escapeshellarg($path));
  1244. break;
  1245. case 'bsd':
  1246. $type = exec('file -Ib '.escapeshellarg($path));
  1247. break;
  1248. default:
  1249. $pinfo = pathinfo($path);
  1250. $ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
  1251. $type = isset($this->_mimeTypes[$ext]) ? $this->_mimeTypes[$ext] : 'unknown;';
  1252. }
  1253. $type = explode(';', $type);
  1254. if ($this->_options['mimeDetect'] != 'internal' && $type[0] == 'application/octet-stream') {
  1255. $pinfo = pathinfo($path);
  1256. $ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
  1257. if (!empty($ext) && !empty($this->_mimeTypes[$ext])) {
  1258. $type[0] = $this->_mimeTypes[$ext];
  1259. }
  1260. }
  1261. return $type[0];
  1262. }
  1263. /************************************************************/
  1264. /** image manipulation **/
  1265. /************************************************************/
  1266. /**
  1267. * Create image thumbnail
  1268. *
  1269. * @param string $img image file
  1270. * @param string $tmb thumbnail name
  1271. * @return bool
  1272. **/
  1273. private function _tmb($img, $tmb)
  1274. {
  1275. if (false == ($s = getimagesize($img))) {
  1276. return false;
  1277. }
  1278. $tmbSize = $this->_options['tmbSize'];
  1279. switch ($this->_options['imgLib']) {
  1280. case 'imagick':
  1281. try {
  1282. $_img = new imagick($img);
  1283. } catch (Exception $e) {
  1284. return false;
  1285. }
  1286. $_img->contrastImage(1);
  1287. return $_img->cropThumbnailImage($tmbSize, $tmbSize) && $_img->writeImage($tmb);
  1288. break;
  1289. case 'mogrify':
  1290. if (@copy($img, $tmb)) {
  1291. list($x, $y, $size) = $this->_cropPos($s[0], $s[1]);
  1292. // exec('mogrify -crop '.$size.'x'.$size.'+'.$x.'+'.$y.' -scale '.$tmbSize.'x'.$tmbSize.'! '.escapeshellarg($tmb), $o, $c);
  1293. exec('mogrify -resize '.$tmbSize.'x'.$tmbSize.'^ -gravity center -extent '.$tmbSize.'x'.$tmbSize.' '.escapeshellarg($tmb), $o, $c);
  1294. if (file_exists($tmb)) {
  1295. return true;
  1296. } elseif ($c == 0) {
  1297. // find tmb for psd and animated gif
  1298. $mime = $this->_mimetype($img);
  1299. if ($mime == 'image/vnd.adobe.photoshop' || $mime = 'image/gif') {
  1300. $pinfo = pathinfo($tmb);
  1301. $test = $pinfo['dirname'].DIRECTORY_SEPARATOR.$pinfo['filename'].'-0.'.$pinfo['extension'];
  1302. if (file_exists($test)) {
  1303. return rename($test, $tmb);
  1304. }
  1305. }
  1306. }
  1307. }
  1308. break;
  1309. case 'gd':
  1310. if ($s['mime'] == 'image/jpeg') {
  1311. $_img = imagecreatefromjpeg($img);
  1312. } elseif ($s['mime'] == 'image/png') {
  1313. $_img = imagecreatefrompng($img);
  1314. } elseif ($s['mime'] == 'image/gif') {
  1315. $_img = imagecreatefromgif($img);
  1316. }
  1317. if (!$_img || false == ($_tmb = imagecreatetruecolor($tmbSize, $tmbSize))) {
  1318. return false;
  1319. }
  1320. list($x, $y, $size) = $this->_cropPos($s[0], $s[1]);
  1321. if (!imagecopyresampled($_tmb, $_img, 0, 0, $x, $y, $tmbSize, $tmbSize, $size, $size)) {
  1322. return false;
  1323. }
  1324. $r = imagepng($_tmb, $tmb, 7);
  1325. imagedestroy($_img);
  1326. imagedestroy($_tmb);
  1327. return $r;
  1328. break;
  1329. }
  1330. }
  1331. /**
  1332. * Remove image thumbnail
  1333. *
  1334. * @param string $img image file
  1335. * @return void
  1336. **/
  1337. private function _rmTmb($img)
  1338. {
  1339. if ($this->_options['tmbDir'] && false != ($tmb = $this->_tmbPath($img)) && file_exists($tmb)) {
  1340. @unlink($tmb);
  1341. }
  1342. }
  1343. /**
  1344. * Return x/y coord for crop image thumbnail
  1345. *
  1346. * @param int $w image width
  1347. * @param int $h image height
  1348. * @return array
  1349. **/
  1350. private function _cropPos($w, $h)
  1351. {
  1352. $x = $y = 0;
  1353. $size = min($w, $h);
  1354. if ($w > $h) {
  1355. $x = ceil(($w - $h)/2);
  1356. } else {
  1357. $y = ceil(($h - $w)/2);
  1358. }
  1359. return array($x, $y, $size);
  1360. }
  1361. /**
  1362. * Resize image
  1363. *
  1364. * @param string $img image path
  1365. * @param int $w image width
  1366. * @param int $h image height
  1367. * @return bool
  1368. **/
  1369. private function _resizeImg($img, $w, $h)
  1370. {
  1371. if (false == ($s = getimagesize($img))) {
  1372. return false;
  1373. }
  1374. switch ($this->_options['imgLib']) {
  1375. case 'imagick':
  1376. if (false != ($_img = new imagick($img))) {
  1377. return $_img->cropThumbnailImage($w, $h) && $_img->writeImage($img);
  1378. }
  1379. break;
  1380. case 'mogrify':
  1381. exec('mogrify -scale '.$w.'x'.$h.'! '.escapeshellarg($img), $o, $c);
  1382. return 0 == $c;
  1383. break;
  1384. case 'gd':
  1385. if ($s['mime'] == 'image/jpeg') {
  1386. $_img = imagecreatefromjpeg($img);
  1387. } elseif ($s['mime'] = 'image/png') {
  1388. $_img = imagecreatefrompng($img);
  1389. } elseif ($s['mime'] = 'image/gif') {
  1390. $_img = imagecreatefromgif($img);
  1391. }
  1392. if (!$_img || false == ($_out = imagecreatetruecolor($w, $h))) {
  1393. return false;
  1394. }
  1395. if (!imagecopyresampled($_out, $_img, 0, 0, 0, 0, $w, $h, $s[0], $s[1])) {
  1396. return false;
  1397. }
  1398. if ($s['mime'] == 'image/jpeg') {
  1399. $r = imagejpeg($_out, $img, 100);
  1400. } else if ($s['mime'] = 'image/png') {
  1401. $r = imagepng($_out, $img, 7);
  1402. } else {
  1403. $r = imagegif($_out, $img, 7);
  1404. }
  1405. imagedestroy($_img);
  1406. imagedestroy($_out);
  1407. return $r;
  1408. break;
  1409. }
  1410. }
  1411. /**
  1412. * Return true if we can create thumbnail for file with this mimetype
  1413. *
  1414. * @param string $mime file mimetype
  1415. * @return bool
  1416. **/
  1417. private function _canCreateTmb($mime)
  1418. {
  1419. if ($this->_options['tmbDir'] && $this->_options['imgLib'] && 0 === strpos($mime, 'image')) {
  1420. if ('gd' == $this->_options['imgLib']) {
  1421. return $mime == 'image/jpeg' || $mime == 'image/png' || $mime == 'image/gif';
  1422. }
  1423. return true;
  1424. }
  1425. }
  1426. /**
  1427. * Return image thumbnail path. For thumbnail return itself
  1428. *
  1429. * @param string $path image path
  1430. * @return string
  1431. **/
  1432. private function _tmbPath($path)
  1433. {
  1434. $tmb = '';
  1435. if ($this->_options['tmbDir']) {
  1436. $tmb = dirname($path) != $this->_options['tmbDir']
  1437. ? $this->_options['tmbDir'].DIRECTORY_SEPARATOR.$this->_hash($path).'.png'
  1438. : $path;
  1439. }
  1440. return $tmb;
  1441. }
  1442. /************************************************************/
  1443. /** access control **/
  1444. /************************************************************/
  1445. /**
  1446. * Return true if file's mimetype is allowed for upload
  1447. *
  1448. * @param string $name file name
  1449. * @param string $tmpName uploaded file tmp name
  1450. * @return bool
  1451. **/
  1452. private function _isUploadAllow($name, $tmpName)
  1453. {
  1454. $mime = $this->_mimetype($this->_options['mimeDetect'] != 'internal' ? $tmpName : $name);
  1455. $allow = false;
  1456. $deny = false;
  1457. if (in_array('all', $this->_options['uploadAllow'])) {
  1458. $allow = true;
  1459. } else {
  1460. foreach ($this->_options['uploadAllow'] as $type) {
  1461. if (0 === strpos($mime, $type)) {
  1462. $allow = true;
  1463. break;
  1464. }
  1465. }
  1466. }
  1467. if (in_array('all', $this->_options['uploadDeny'])) {
  1468. $deny = true;
  1469. } else {
  1470. foreach ($this->_options['uploadDeny'] as $type) {
  1471. if (0 === strpos($mime, $type)) {
  1472. $deny = true;
  1473. break;
  1474. }
  1475. }
  1476. }
  1477. return 0 === strpos($this->_options['uploadOrder'], 'allow') ? $allow && !$deny : $allow || !$deny;
  1478. }
  1479. /**
  1480. * Return true if file name is not . or ..
  1481. * If file name begins with . return value according to $this->_options['dotFiles']
  1482. *
  1483. * @param string $file file name
  1484. * @return bool
  1485. **/
  1486. private function _isAccepted($file)
  1487. {
  1488. if ('.' == $file || '..' == $file) {
  1489. return false;
  1490. }
  1491. if (!$this->_options['dotFiles'] && '.' == substr($file, 0, 1)) {
  1492. return false;
  1493. }
  1494. return true;
  1495. }
  1496. /**
  1497. * Return true if requeired action allowed to file/folder
  1498. *
  1499. * @param string $path file/folder path
  1500. * @param string $action action name (read/write/rm)
  1501. * @return void
  1502. **/
  1503. private function _isAllowed($path, $action) {
  1504. switch ($action) {
  1505. case 'read':
  1506. if (!is_readable($path)) {
  1507. return false;
  1508. }
  1509. break;
  1510. case 'write':
  1511. if (!is_writable($path)) {
  1512. return false;
  1513. }
  1514. break;
  1515. case 'rm':
  1516. if (!is_writable(dirname($path))) {
  1517. return false;
  1518. }
  1519. break;
  1520. }
  1521. // if ($this->_options['aclObj']) {
  1522. //
  1523. // }
  1524. $path = substr($path, strlen($this->_options['root'])+1);
  1525. // echo "$path\n";
  1526. foreach ($this->_options['perms'] as $regex => $rules) {
  1527. if (preg_match($regex, $path)) {
  1528. if (isset($rules[$action])) {
  1529. return $rules[$action];
  1530. }
  1531. }
  1532. }
  1533. return isset($this->_options['defaults'][$action]) ? $this->_options['defaults'][$action] : false;
  1534. }
  1535. /************************************************************/
  1536. /** utilites **/
  1537. /************************************************************/
  1538. /**
  1539. * Return image manipalation library name
  1540. *
  1541. * @return string
  1542. **/
  1543. private function _getImgLib()
  1544. {
  1545. if (extension_loaded('imagick')) {
  1546. return 'imagick';
  1547. } elseif (function_exists('exec')) {
  1548. exec('mogrify --version', $o, $c);
  1549. if ($c == 0) {
  1550. return 'mogrify';
  1551. }
  1552. }
  1553. return function_exists('gd_info') ? 'gd' : '';
  1554. }
  1555. /**
  1556. * Return list of available archivers
  1557. *
  1558. * @return array
  1559. **/
  1560. private function _checkArchivers()
  1561. {
  1562. if (!function_exists('exec')) {
  1563. $this->_options['archivers'] = $this->_options['archive'] = array();
  1564. return;
  1565. }
  1566. $arcs = array(
  1567. 'create' => array(),
  1568. 'extract' => array()
  1569. );
  1570. exec('tar --version', $o, $ctar);
  1571. if ($ctar == 0) {
  1572. $arcs['create']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
  1573. $arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
  1574. $test = exec('gzip --version', $o, $c);
  1575. if ($c == 0) {
  1576. $arcs['create']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
  1577. $arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
  1578. }
  1579. $test = exec('bzip2 --version', $o, $c);
  1580. if ($c == 0) {
  1581. $arcs['create']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
  1582. $arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
  1583. }
  1584. }
  1585. exec('zip --version', $o, $c);
  1586. if ($c == 0) {
  1587. $arcs['create']['application/zip'] = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
  1588. }
  1589. exec('unzip --help', $o, $c);
  1590. if ($c == 0) {
  1591. $arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '', 'ext' => 'zip');
  1592. }
  1593. exec('rar --version', $o, $c);
  1594. if ($c == 0) {
  1595. $arcs['create']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'a inul', 'ext' => 'rar');
  1596. $arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'arg…

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