PageRenderTime 72ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/www/protected/modules/filemanager/extensions/elfinder/elFinder.class.php

https://bitbucket.org/badenkov/demo
PHP | 1995 lines | 1437 code | 170 blank | 388 comment | 495 complexity | 7312664d2571d10eb8289cfb9b8f0b99 MD5 | raw file
Possible License(s): Apache-2.0, MIT, LGPL-2.1, BSD-2-Clause, CC-BY-SA-3.0, BSD-3-Clause

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

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