PageRenderTime 49ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/manager/media/browser/mcpuk/core/browser.php

https://github.com/modxcms/evolution
PHP | 1192 lines | 1030 code | 79 blank | 83 comment | 152 complexity | b424449c9b52b193d2f0680d86c4cfd1 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MIT, BSD-2-Clause, Apache-2.0, BSD-3-Clause
  1. <?php
  2. /** This file is part of KCFinder project
  3. *
  4. * @desc Browser actions class
  5. * @package KCFinder
  6. * @version 2.54
  7. * @author Pavel Tzonkov <sunhater@sunhater.com>
  8. * @copyright 2010-2014 KCFinder Project
  9. * @license http://www.opensource.org/licenses/gpl-2.0.php GPLv2
  10. * @license http://www.opensource.org/licenses/lgpl-2.1.php LGPLv2
  11. * @link http://kcfinder.sunhater.com
  12. */
  13. class browser extends uploader
  14. {
  15. protected $action;
  16. protected $thumbsDir;
  17. protected $thumbsTypeDir;
  18. /**
  19. * browser constructor.
  20. * @param DocumentParser $modx
  21. */
  22. public function __construct(DocumentParser $modx)
  23. {
  24. parent::__construct($modx);
  25. if (isset($this->post['dir'])) {
  26. $dir = $this->checkInputDir($this->post['dir'], true, false);
  27. if ($dir === false) {
  28. unset($this->post['dir']);
  29. }
  30. $this->post['dir'] = $dir;
  31. }
  32. if (isset($this->get['dir'])) {
  33. $dir = $this->checkInputDir($this->get['dir'], true, false);
  34. if ($dir === false) {
  35. unset($this->get['dir']);
  36. }
  37. $this->get['dir'] = $dir;
  38. }
  39. $thumbsDir = $this->config['uploadDir'] . "/" . $this->config['thumbsDir'];
  40. if ((
  41. !is_dir($thumbsDir) &&
  42. !@mkdir($thumbsDir, $this->config['dirPerms'])
  43. ) ||
  44. !is_readable($thumbsDir) ||
  45. !dir::isWritable($thumbsDir) ||
  46. (
  47. !is_dir("$thumbsDir/{$this->type}") &&
  48. !@mkdir("$thumbsDir/{$this->type}", $this->config['dirPerms'])
  49. )
  50. ) {
  51. $this->errorMsg("Cannot access or create thumbnails folder.");
  52. }
  53. $this->thumbsDir = $thumbsDir;
  54. $this->thumbsTypeDir = "$thumbsDir/{$this->type}";
  55. // Remove temporary zip downloads if exists
  56. $files = dir::content($this->config['uploadDir'], array(
  57. 'types' => "file",
  58. 'pattern' => '/^.*\.zip$/i'
  59. ));
  60. if (is_array($files) && count($files)) {
  61. $time = time();
  62. foreach ($files as $file) {
  63. if (is_file($file) && ($time - filemtime($file) > 3600)) {
  64. unlink($file);
  65. }
  66. }
  67. }
  68. if (isset($this->get['theme']) &&
  69. ($this->get['theme'] == basename($this->get['theme'])) &&
  70. is_dir("themes/{$this->get['theme']}")
  71. ) {
  72. $this->config['theme'] = $this->get['theme'];
  73. }
  74. }
  75. /**
  76. *
  77. */
  78. public function action()
  79. {
  80. $act = isset($this->get['act']) ? $this->get['act'] : "browser";
  81. if (!preg_match('@^[0-9a-zA-Z_]+$@', $act)) {
  82. $this->errorMsg("Unknown error.");
  83. }
  84. if (!method_exists($this, "act_$act")) {
  85. $act = "browser";
  86. }
  87. $this->action = $act;
  88. $method = "act_$act";
  89. if ($this->config['disabled']) {
  90. $message = $this->label("You don't have permissions to browse server.");
  91. if (in_array($act, array("browser", "upload")) ||
  92. (substr($act, 0, 8) == "download")
  93. ) {
  94. $this->backMsg($message);
  95. } else {
  96. header("Content-Type: text/plain; charset={$this->charset}");
  97. die(json_encode(array('error' => $message)));
  98. }
  99. }
  100. if (!isset($this->session['dir'])) {
  101. $this->session['dir'] = $this->type;
  102. } else {
  103. $type = $this->getTypeFromPath($this->session['dir']);
  104. $dir = $this->config['uploadDir'] . "/" . $this->session['dir'];
  105. if (($type != $this->type) || !is_dir($dir) || !is_readable($dir)) {
  106. $this->session['dir'] = $this->type;
  107. }
  108. }
  109. $this->session['dir'] = path::normalize($this->session['dir']);
  110. if ($act == "browser") {
  111. header("X-UA-Compatible: chrome=1");
  112. header("Content-Type: text/html; charset={$this->charset}");
  113. } elseif (
  114. (substr($act, 0, 8) != "download") &&
  115. !in_array($act, array("thumb", "upload"))
  116. ) {
  117. header("Content-Type: text/plain; charset={$this->charset}");
  118. }
  119. $return = $this->$method();
  120. echo ($return === true)
  121. ? '{}'
  122. : $return;
  123. }
  124. /**
  125. * @return string
  126. */
  127. protected function act_browser()
  128. {
  129. if (isset($this->get['dir']) &&
  130. is_dir("{$this->typeDir}/{$this->get['dir']}") &&
  131. is_readable("{$this->typeDir}/{$this->get['dir']}")
  132. ) {
  133. $this->session['dir'] = path::normalize("{$this->type}/{$this->get['dir']}");
  134. }
  135. return $this->output();
  136. }
  137. /**
  138. * @return string
  139. */
  140. protected function act_init()
  141. {
  142. $tree = $this->getDirInfo($this->typeDir);
  143. $tree['dirs'] = $this->getTree($this->session['dir']);
  144. if (!is_array($tree['dirs']) || !count($tree['dirs'])) {
  145. unset($tree['dirs']);
  146. }
  147. $files = $this->getFiles($this->session['dir']);
  148. $dirWritable = dir::isWritable("{$this->config['uploadDir']}/{$this->session['dir']}");
  149. $data = array(
  150. 'tree' => &$tree,
  151. 'files' => &$files,
  152. 'dirWritable' => $dirWritable
  153. );
  154. return json_encode($data);
  155. }
  156. /**
  157. *
  158. */
  159. protected function act_thumb()
  160. {
  161. $this->getDir($this->get['dir'], true);
  162. if (!isset($this->get['file']) || !isset($this->get['dir'])) {
  163. $this->sendDefaultThumb();
  164. }
  165. $file = $this->get['file'];
  166. if (basename($file) != $file) {
  167. $this->sendDefaultThumb();
  168. }
  169. $file = "{$this->thumbsDir}/{$this->type}/{$this->get['dir']}/$file";
  170. if (!is_file($file) || !is_readable($file)) {
  171. $file = "{$this->config['uploadDir']}/{$this->type}/{$this->get['dir']}/" . basename($file);
  172. if (!is_file($file) || !is_readable($file)) {
  173. $this->sendDefaultThumb($file);
  174. }
  175. $image = image::factory($this->imageDriver, $file);
  176. if ($image->initError) {
  177. $this->sendDefaultThumb($file);
  178. }
  179. list($tmp, $tmp, $type) = getimagesize($file);
  180. if (in_array($type, array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG)) &&
  181. ($image->width <= $this->config['thumbWidth']) &&
  182. ($image->height <= $this->config['thumbHeight'])
  183. ) {
  184. $mime =
  185. ($type == IMAGETYPE_GIF) ? "gif" : (
  186. ($type == IMAGETYPE_PNG) ? "png" : "jpeg");
  187. $mime = "image/$mime";
  188. httpCache::file($file, $mime);
  189. } else {
  190. $this->sendDefaultThumb($file);
  191. }
  192. }
  193. httpCache::file($file, "image/jpeg");
  194. }
  195. /**
  196. * @return string
  197. */
  198. protected function act_expand()
  199. {
  200. return json_encode(array('dirs' => $this->getDirs($this->postDir())));
  201. }
  202. /**
  203. * @return string
  204. */
  205. protected function act_chDir()
  206. {
  207. $this->postDir(); // Just for existing check
  208. $this->session['dir'] = $this->type . "/" . $this->post['dir'];
  209. $dirWritable = dir::isWritable("{$this->config['uploadDir']}/{$this->session['dir']}");
  210. return json_encode(array(
  211. 'files' => $this->getFiles($this->session['dir']),
  212. 'dirWritable' => $dirWritable
  213. ));
  214. }
  215. /**
  216. * @return bool
  217. */
  218. protected function act_newDir()
  219. {
  220. if (!$this->config['access']['dirs']['create'] ||
  221. !isset($this->post['dir']) ||
  222. !isset($this->post['newDir'])
  223. ) {
  224. $this->errorMsg("Unknown error.");
  225. }
  226. $dir = $this->postDir();
  227. $newDir = $this->normalizeDirname(trim($this->post['newDir']));
  228. if (!strlen($newDir)) {
  229. $this->errorMsg("Please enter new folder name.");
  230. }
  231. if (preg_match('/[\/\\\\]/s', $newDir)) {
  232. $this->errorMsg("Unallowable characters in folder name.");
  233. }
  234. if (substr($newDir, 0, 1) == ".") {
  235. $this->errorMsg("Folder name shouldn't begins with '.'");
  236. }
  237. if (file_exists("$dir/$newDir")) {
  238. $this->errorMsg("A file or folder with that name already exists.");
  239. }
  240. if (!@mkdir("$dir/$newDir", $this->config['dirPerms'])) {
  241. $this->errorMsg("Cannot create {dir} folder.", array('dir' => $newDir));
  242. }
  243. return true;
  244. }
  245. /**
  246. * @return string
  247. */
  248. protected function act_renameDir()
  249. {
  250. if (!$this->config['access']['dirs']['rename'] ||
  251. !isset($this->post['dir']) ||
  252. !isset($this->post['newName'])
  253. ) {
  254. $this->errorMsg("Unknown error.");
  255. }
  256. $dir = $this->postDir();
  257. $newName = $this->normalizeDirname(trim($this->post['newName']));
  258. $evtOut = $this->modx->invokeEvent('OnBeforeFileBrowserRename', array(
  259. 'element' => 'dir',
  260. 'filepath' => realpath($dir),
  261. 'newname' => &$newName
  262. ));
  263. if (!strlen($newName)) {
  264. $this->errorMsg("Please enter new folder name.");
  265. }
  266. if (preg_match('/[\/\\\\]/s', $newName)) {
  267. $this->errorMsg("Unallowable characters in folder name.");
  268. }
  269. if (substr($newName, 0, 1) == ".") {
  270. $this->errorMsg("Folder name shouldn't begins with '.'");
  271. }
  272. if (is_array($evtOut) && !empty($evtOut)) {
  273. $this->errorMsg(implode('\n', $evtOut));
  274. }
  275. if (!@rename($dir, dirname($dir) . "/$newName")) {
  276. $this->errorMsg("Cannot rename the folder.");
  277. }
  278. $thumbDir = "$this->thumbsTypeDir/{$this->post['dir']}";
  279. if (is_dir($thumbDir)) {
  280. @rename($thumbDir, dirname($thumbDir) . "/$newName");
  281. }
  282. $this->modx->invokeEvent('OnFileBrowserRename', array(
  283. 'element' => 'dir',
  284. 'filepath' => realpath($dir),
  285. 'newname' => $newName
  286. ));
  287. return json_encode(array('name' => $newName));
  288. }
  289. /**
  290. * @return bool
  291. */
  292. protected function act_deleteDir()
  293. {
  294. if (!$this->config['access']['dirs']['delete'] ||
  295. !isset($this->post['dir']) ||
  296. !strlen(trim($this->post['dir']))
  297. ) {
  298. $this->errorMsg("Unknown error.");
  299. }
  300. $dir = $this->postDir();
  301. if (!dir::isWritable($dir)) {
  302. $this->errorMsg("Cannot delete the folder.");
  303. }
  304. $evtOut = $this->modx->invokeEvent('OnBeforeFileBrowserDelete', array(
  305. 'element' => 'dir',
  306. 'filepath' => realpath($dir)
  307. ));
  308. if (is_array($evtOut) && !empty($evtOut)) {
  309. die(json_encode(array('error' => $evtOut)));
  310. }
  311. $result = !dir::prune($dir, false);
  312. if (is_array($result) && count($result)) {
  313. $this->errorMsg("Failed to delete {count} files/folders.",
  314. array('count' => count($result)));
  315. }
  316. $thumbDir = "$this->thumbsTypeDir/{$this->post['dir']}";
  317. if (is_dir($thumbDir)) {
  318. dir::prune($thumbDir);
  319. }
  320. $this->modx->invokeEvent('OnFileBrowserDelete', array(
  321. 'element' => 'dir',
  322. 'filepath' => realpath($dir)
  323. ));
  324. return true;
  325. }
  326. /**
  327. * @return string
  328. */
  329. protected function act_upload()
  330. {
  331. $response = array('success' => false, 'message' => $this->label("Unknown error."));
  332. if (!$this->config['access']['files']['upload'] ||
  333. !isset($this->post['dir'])
  334. ) {
  335. return json_encode($response);
  336. }
  337. $dir = $this->postDir();
  338. if (!dir::isWritable($dir)) {
  339. $response['message'] = $this->label("Cannot access or write to upload folder.");
  340. return json_encode($response);
  341. }
  342. $response = $this->moveUploadFile($this->file, $dir);
  343. return json_encode($response);
  344. }
  345. /**
  346. *
  347. */
  348. protected function act_download()
  349. {
  350. $dir = $this->postDir();
  351. if (!isset($this->post['dir']) ||
  352. !isset($this->post['file']) ||
  353. strpos($this->post['file'], '../') !== false ||
  354. (false === ($file = "$dir/{$this->post['file']}")) ||
  355. !file_exists($file) || !is_readable($file)
  356. ) {
  357. $this->errorMsg("Unknown error.");
  358. }
  359. header("Pragma: public");
  360. header("Expires: 0");
  361. header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  362. header("Cache-Control: private", false);
  363. header("Content-Type: application/octet-stream");
  364. header('Content-Disposition: attachment; filename="' . str_replace('"', "_", $this->post['file']) . '"');
  365. header("Content-Transfer-Encoding:­ binary");
  366. header("Content-Length: " . filesize($file));
  367. readfile($file);
  368. die;
  369. }
  370. /**
  371. * @return bool
  372. */
  373. protected function act_rename()
  374. {
  375. $dir = $this->postDir();
  376. if (!$this->config['access']['files']['rename'] ||
  377. !isset($this->post['dir']) ||
  378. !isset($this->post['file']) ||
  379. strpos($this->post['file'], '../') !== false ||
  380. !isset($this->post['newName']) ||
  381. (false === ($file = "$dir/{$this->post['file']}")) ||
  382. !file_exists($file) || !is_readable($file) || !file::isWritable($file)
  383. ) {
  384. $this->errorMsg("Unknown error.");
  385. }
  386. if (isset($this->config['denyExtensionRename']) &&
  387. $this->config['denyExtensionRename'] &&
  388. (file::getExtension($this->post['file'], true) !==
  389. file::getExtension($this->post['newName'], true)
  390. )
  391. ) {
  392. $this->errorMsg("You cannot rename the extension of files!");
  393. }
  394. $newName = $this->normalizeFilename(trim($this->post['newName']));
  395. $evtOut = $this->modx->invokeEvent('OnBeforeFileBrowserRename', array(
  396. 'element' => 'file',
  397. 'filepath' => $dir,
  398. 'filename' => $this->post['file'],
  399. 'newname' => &$newName
  400. ));
  401. if (!strlen($newName)) {
  402. $this->errorMsg("Please enter new file name.");
  403. }
  404. if (preg_match('/[\/\\\\]/s', $newName)) {
  405. $this->errorMsg("Unallowable characters in file name.");
  406. }
  407. if (substr($newName, 0, 1) == ".") {
  408. $this->errorMsg("File name shouldn't begins with '.'");
  409. }
  410. $_newName = $newName;
  411. $newName = "$dir/$newName";
  412. if (file_exists($newName)) {
  413. $this->errorMsg("A file or folder with that name already exists.");
  414. }
  415. $ext = file::getExtension($newName);
  416. if (!$this->validateExtension($ext, $this->type)) {
  417. $this->errorMsg("Denied file extension.");
  418. }
  419. if (is_array($evtOut) && !empty($evtOut)) {
  420. $this->errorMsg(implode('\n', $evtOut));
  421. }
  422. if (!@rename($file, $newName)) {
  423. $this->errorMsg("Unknown error.");
  424. }
  425. $this->modx->invokeEvent('OnFileBrowserRename', array(
  426. 'element' => 'file',
  427. 'filepath' => $dir,
  428. 'filename' => $this->post['file'],
  429. 'newname' => $_newName
  430. ));
  431. $thumbDir = "{$this->thumbsTypeDir}/{$this->post['dir']}";
  432. $thumbFile = "$thumbDir/{$this->post['file']}";
  433. if (file_exists($thumbFile)) {
  434. @rename($thumbFile, "$thumbDir/" . basename($newName));
  435. }
  436. return true;
  437. }
  438. /**
  439. * @return bool
  440. */
  441. protected function act_delete()
  442. {
  443. $dir = $this->postDir();
  444. if (!$this->config['access']['files']['delete'] ||
  445. !isset($this->post['dir']) ||
  446. !isset($this->post['file']) ||
  447. strpos($this->post['file'], '../') !== false ||
  448. (false === ($file = "$dir/{$this->post['file']}")) ||
  449. !file_exists($file) || !is_readable($file) || !file::isWritable($file)
  450. ) {
  451. $this->errorMsg("Cannot delete '{file}'.", array('file' => basename($file)));
  452. }
  453. $evtOut = $this->modx->invokeEvent('OnBeforeFileBrowserDelete', array(
  454. 'element' => 'file',
  455. 'filename' => $this->post['file'],
  456. 'filepath' => realpath($dir)
  457. ));
  458. if (is_array($evtOut) && !empty($evtOut)) {
  459. die(json_encode(array('error' => $evtOut)));
  460. }
  461. @unlink($file);
  462. $thumb = "{$this->thumbsTypeDir}/{$this->post['dir']}/{$this->post['file']}";
  463. if (file_exists($thumb)) {
  464. @unlink($thumb);
  465. }
  466. $this->modx->invokeEvent('OnFileBrowserDelete', array(
  467. 'element' => 'file',
  468. 'filename' => $this->post['file'],
  469. 'filepath' => realpath($dir)
  470. ));
  471. return true;
  472. }
  473. /**
  474. * @return bool|string
  475. */
  476. protected function act_cp_cbd()
  477. {
  478. $dir = $this->postDir();
  479. if (!$this->config['access']['files']['copy'] ||
  480. !isset($this->post['dir']) ||
  481. !is_dir($dir) || !is_readable($dir) || !dir::isWritable($dir) ||
  482. !isset($this->post['files']) || !is_array($this->post['files']) ||
  483. !count($this->post['files'])
  484. ) {
  485. $this->errorMsg("Unknown error.");
  486. }
  487. $error = array();
  488. foreach ($this->post['files'] as $file) {
  489. $file = path::normalize($file);
  490. if (substr($file, 0, 1) == ".") {
  491. continue;
  492. }
  493. $type = explode("/", $file);
  494. $type = $type[0];
  495. if ($type != $this->type) {
  496. continue;
  497. }
  498. $path = "{$this->config['uploadDir']}/$file";
  499. $base = basename($file);
  500. $replace = array('file' => $base);
  501. $ext = file::getExtension($base);
  502. $evtOut = $this->modx->invokeEvent('OnBeforeFileBrowserCopy', array(
  503. 'oldpath' => $path,
  504. 'filename' => $base,
  505. 'newpath' => realpath($dir)
  506. ));
  507. if (is_array($evtOut) && !empty($evtOut)) {
  508. $error[] = implode("\n", $evtOut);
  509. } elseif (!file_exists($path)) {
  510. $error[] = $this->label("The file '{file}' does not exist.", $replace);
  511. } elseif (substr($base, 0, 1) == ".") {
  512. $error[] = "$base: " . $this->label("File name shouldn't begins with '.'");
  513. } elseif (!$this->validateExtension($ext, $type)) {
  514. $error[] = "$base: " . $this->label("Denied file extension.");
  515. } elseif (file_exists("$dir/$base")) {
  516. $error[] = "$base: " . $this->label("A file or folder with that name already exists.");
  517. } elseif (!is_readable($path) || !is_file($path)) {
  518. $error[] = $this->label("Cannot read '{file}'.", $replace);
  519. } elseif (!@copy($path, "$dir/$base")) {
  520. $error[] = $this->label("Cannot copy '{file}'.", $replace);
  521. } else {
  522. if (function_exists("chmod")) {
  523. @chmod("$dir/$base", $this->config['filePerms']);
  524. }
  525. $this->modx->invokeEvent('OnFileBrowserCopy', array(
  526. 'oldpath' => $path,
  527. 'filename' => $base,
  528. 'newpath' => realpath($dir)
  529. ));
  530. $fromThumb = "{$this->thumbsDir}/$file";
  531. if (is_file($fromThumb) && is_readable($fromThumb)) {
  532. $toThumb = "{$this->thumbsTypeDir}/{$this->post['dir']}";
  533. if (!is_dir($toThumb)) {
  534. @mkdir($toThumb, $this->config['dirPerms'], true);
  535. }
  536. $toThumb .= "/$base";
  537. @copy($fromThumb, $toThumb);
  538. }
  539. }
  540. }
  541. if (count($error)) {
  542. return json_encode(array('error' => $error));
  543. }
  544. return true;
  545. }
  546. /**
  547. * @return bool|string
  548. */
  549. protected function act_mv_cbd()
  550. {
  551. $dir = $this->postDir();
  552. if (!$this->config['access']['files']['move'] ||
  553. !isset($this->post['dir']) ||
  554. !is_dir($dir) || !is_readable($dir) || !dir::isWritable($dir) ||
  555. !isset($this->post['files']) || !is_array($this->post['files']) ||
  556. !count($this->post['files'])
  557. ) {
  558. $this->errorMsg("Unknown error.");
  559. }
  560. $error = array();
  561. foreach ($this->post['files'] as $file) {
  562. $file = path::normalize($file);
  563. if (substr($file, 0, 1) == ".") {
  564. continue;
  565. }
  566. $type = explode("/", $file);
  567. $type = $type[0];
  568. if ($type != $this->type) {
  569. continue;
  570. }
  571. $path = "{$this->config['uploadDir']}/$file";
  572. $base = basename($file);
  573. $replace = array('file' => $base);
  574. $ext = file::getExtension($base);
  575. $evtOut = $this->modx->invokeEvent('OnBeforeFileBrowserMove', array(
  576. 'oldpath' => $path,
  577. 'filename' => $base,
  578. 'newpath' => realpath($dir)
  579. ));
  580. if (is_array($evtOut) && !empty($evtOut)) {
  581. $error[] = implode("\n", $evtOut);
  582. } elseif (!file_exists($path)) {
  583. $error[] = $this->label("The file '{file}' does not exist.", $replace);
  584. } elseif (substr($base, 0, 1) == ".") {
  585. $error[] = "$base: " . $this->label("File name shouldn't begins with '.'");
  586. } elseif (!$this->validateExtension($ext, $type)) {
  587. $error[] = "$base: " . $this->label("Denied file extension.");
  588. } elseif (file_exists("$dir/$base")) {
  589. $error[] = "$base: " . $this->label("A file or folder with that name already exists.");
  590. } elseif (!is_readable($path) || !is_file($path)) {
  591. $error[] = $this->label("Cannot read '{file}'.", $replace);
  592. } elseif (!file::isWritable($path) || !@rename($path, "$dir/$base")) {
  593. $error[] = $this->label("Cannot move '{file}'.", $replace);
  594. } else {
  595. if (function_exists("chmod")) {
  596. @chmod("$dir/$base", $this->config['filePerms']);
  597. }
  598. $fromThumb = "{$this->thumbsDir}/$file";
  599. if (is_file($fromThumb) && is_readable($fromThumb)) {
  600. $toThumb = "{$this->thumbsTypeDir}/{$this->post['dir']}";
  601. if (!is_dir($toThumb)) {
  602. @mkdir($toThumb, $this->config['dirPerms'], true);
  603. }
  604. $toThumb .= "/$base";
  605. @rename($fromThumb, $toThumb);
  606. }
  607. $this->modx->invokeEvent('OnFileBrowserMove', array(
  608. 'oldpath' => $path,
  609. 'filename' => $base,
  610. 'newpath' => realpath($dir)
  611. ));
  612. }
  613. }
  614. if (count($error)) {
  615. return json_encode(array('error' => $error));
  616. }
  617. return true;
  618. }
  619. /**
  620. * @return bool|string
  621. */
  622. protected function act_rm_cbd()
  623. {
  624. if (!$this->config['access']['files']['delete'] ||
  625. !isset($this->post['files']) ||
  626. !is_array($this->post['files']) ||
  627. !count($this->post['files'])
  628. ) {
  629. $this->errorMsg("Unknown error.");
  630. }
  631. $error = array();
  632. foreach ($this->post['files'] as $file) {
  633. $file = path::normalize($file);
  634. if (substr($file, 0, 1) == ".") {
  635. continue;
  636. }
  637. $type = explode("/", $file);
  638. $type = $type[0];
  639. if ($type != $this->type) {
  640. continue;
  641. }
  642. $path = "{$this->config['uploadDir']}/$file";
  643. $base = basename($file);
  644. $filepath = str_replace('/' . $base, '', $path);
  645. $replace = array('file' => $base);
  646. if (!is_file($path)) {
  647. $error[] = $this->label("The file '{file}' does not exist.", $replace);
  648. } else {
  649. $evtOut = $this->modx->invokeEvent('OnBeforeFileBrowserDelete', array(
  650. 'element' => 'file',
  651. 'filename' => $base,
  652. 'filepath' => $filepath
  653. ));
  654. if (is_array($evtOut) && !empty($evtOut)) {
  655. $error[] = implode("\n", $evtOut);
  656. } else {
  657. if (!@unlink($path)) {
  658. $error[] = $this->label("Cannot delete '{file}'.", $replace);
  659. } else {
  660. $this->modx->invokeEvent('OnFileBrowserDelete', array(
  661. 'element' => 'file',
  662. 'filename' => $base,
  663. 'filepath' => $filepath
  664. ));
  665. $thumb = "{$this->thumbsDir}/$file";
  666. if (is_file($thumb)) {
  667. @unlink($thumb);
  668. }
  669. }
  670. }
  671. }
  672. }
  673. if (count($error)) {
  674. return json_encode(array('error' => $error));
  675. }
  676. return true;
  677. }
  678. /**
  679. * @throws Exception
  680. */
  681. protected function act_downloadDir()
  682. {
  683. $dir = $this->postDir();
  684. if (!isset($this->post['dir']) || $this->config['denyZipDownload']) {
  685. $this->errorMsg("Unknown error.");
  686. }
  687. $filename = basename($dir) . ".zip";
  688. do {
  689. $file = md5(time() . session_id());
  690. $file = "{$this->config['uploadDir']}/$file.zip";
  691. } while (file_exists($file));
  692. new zipFolder($file, $dir);
  693. header("Content-Type: application/x-zip");
  694. header('Content-Disposition: attachment; filename="' . str_replace('"', "_", $filename) . '"');
  695. header("Content-Length: " . filesize($file));
  696. readfile($file);
  697. unlink($file);
  698. die;
  699. }
  700. /**
  701. *
  702. */
  703. protected function act_downloadSelected()
  704. {
  705. $dir = $this->postDir();
  706. if (!isset($this->post['dir']) ||
  707. !isset($this->post['files']) ||
  708. !is_array($this->post['files']) ||
  709. $this->config['denyZipDownload']
  710. ) {
  711. $this->errorMsg("Unknown error.");
  712. }
  713. $zipFiles = array();
  714. foreach ($this->post['files'] as $file) {
  715. $file = path::normalize($file);
  716. if ((substr($file, 0, 1) == ".") || (strpos($file, '/') !== false)) {
  717. continue;
  718. }
  719. $file = "$dir/$file";
  720. if (!is_file($file) || !is_readable($file)) {
  721. continue;
  722. }
  723. $zipFiles[] = $file;
  724. }
  725. do {
  726. $file = md5(time() . session_id());
  727. $file = "{$this->config['uploadDir']}/$file.zip";
  728. } while (file_exists($file));
  729. $zip = new ZipArchive();
  730. $res = $zip->open($file, ZipArchive::CREATE);
  731. if ($res === true) {
  732. foreach ($zipFiles as $cfile) {
  733. $zip->addFile($cfile, basename($cfile));
  734. }
  735. $zip->close();
  736. }
  737. header("Content-Type: application/x-zip");
  738. header('Content-Disposition: attachment; filename="selected_files_' . basename($file) . '"');
  739. header("Content-Length: " . filesize($file));
  740. readfile($file);
  741. unlink($file);
  742. die;
  743. }
  744. /**
  745. *
  746. */
  747. protected function act_downloadClipboard()
  748. {
  749. if (!isset($this->post['files']) ||
  750. !is_array($this->post['files']) ||
  751. $this->config['denyZipDownload']
  752. ) {
  753. $this->errorMsg("Unknown error.");
  754. }
  755. $zipFiles = array();
  756. foreach ($this->post['files'] as $file) {
  757. $file = path::normalize($file);
  758. if ((substr($file, 0, 1) == ".")) {
  759. continue;
  760. }
  761. $type = explode("/", $file);
  762. $type = $type[0];
  763. if ($type != $this->type) {
  764. continue;
  765. }
  766. $file = $this->config['uploadDir'] . "/$file";
  767. if (!is_file($file) || !is_readable($file)) {
  768. continue;
  769. }
  770. $zipFiles[] = $file;
  771. }
  772. do {
  773. $file = md5(time() . session_id());
  774. $file = "{$this->config['uploadDir']}/$file.zip";
  775. } while (file_exists($file));
  776. $zip = new ZipArchive();
  777. $res = $zip->open($file, ZipArchive::CREATE);
  778. if ($res === true) {
  779. foreach ($zipFiles as $cfile) {
  780. $zip->addFile($cfile, basename($cfile));
  781. }
  782. $zip->close();
  783. }
  784. header("Content-Type: application/x-zip");
  785. header('Content-Disposition: attachment; filename="clipboard_' . basename($file) . '"');
  786. header("Content-Length: " . filesize($file));
  787. readfile($file);
  788. unlink($file);
  789. die;
  790. }
  791. /**
  792. * @param $file
  793. * @param $dir
  794. * @return array
  795. */
  796. protected function moveUploadFile($file, $dir)
  797. {
  798. $response = array('success' => false, 'message' => $this->label('Unknown error.'));
  799. $message = $this->checkUploadedFile($file);
  800. if ($message !== true) {
  801. if (isset($file['tmp_name'])) {
  802. @unlink($file['tmp_name']);
  803. }
  804. $response['message'] = $message;
  805. return $response;
  806. }
  807. $evtOut = $this->modx->invokeEvent('OnBeforeFileBrowserUpload', array(
  808. 'file' => &$file,
  809. 'filepath' => realpath($dir)
  810. ));
  811. if (is_array($evtOut) && !empty($evtOut)) {
  812. $response['message'] = $evtOut;
  813. return $response;
  814. }
  815. $filename = $this->normalizeFilename($file['name']);
  816. $target = "$dir/" . file::getInexistantFilename($filename, $dir);
  817. if (!@move_uploaded_file($file['tmp_name'], $target) &&
  818. !@rename($file['tmp_name'], $target) &&
  819. !@copy($file['tmp_name'], $target)
  820. ) {
  821. @unlink($file['tmp_name']);
  822. $response['message'] = $this->label("Cannot move uploaded file to target folder.");
  823. return $response;
  824. } elseif (function_exists('chmod')) {
  825. chmod($target, $this->config['filePerms']);
  826. }
  827. $this->modx->invokeEvent('OnFileBrowserUpload', array(
  828. 'filepath' => realpath($dir),
  829. 'filename' => str_replace("/", "", str_replace($dir, "", realpath($target)))
  830. ));
  831. $this->makeThumb($target);
  832. $response['success'] = true;
  833. return $response;
  834. }
  835. /**
  836. * @param null $file
  837. */
  838. protected function sendDefaultThumb($file = null)
  839. {
  840. if ($file !== null) {
  841. $ext = file::getExtension($file);
  842. $thumb = "themes/{$this->config['theme']}/img/files/big/$ext.png";
  843. }
  844. if (!isset($thumb) || !file_exists($thumb)) {
  845. $thumb = "themes/{$this->config['theme']}/img/files/big/..png";
  846. }
  847. header("Content-Type: image/png");
  848. readfile($thumb);
  849. die;
  850. }
  851. /**
  852. * @param $dir
  853. * @return array
  854. */
  855. protected function getFiles($dir)
  856. {
  857. $thumbDir = "{$this->config['uploadDir']}/{$this->config['thumbsDir']}/$dir";
  858. $dir = "{$this->config['uploadDir']}/$dir";
  859. $return = array();
  860. $files = dir::content($dir, array('types' => "file"));
  861. if ($files === false) {
  862. return $return;
  863. }
  864. foreach ($files as $file) {
  865. $ext = file::getExtension($file);
  866. $smallThumb = false;
  867. $preview = false;
  868. if (in_array(strtolower($ext), array('png', 'jpg', 'gif', 'jpeg'))) {
  869. $size = @getimagesize($file);
  870. if (is_array($size) && count($size)) {
  871. $preview = true;
  872. if (!$this->config['noThumbnailsRecreation']) {
  873. $thumb_file = "$thumbDir/" . basename($file);
  874. if (!is_file($thumb_file) || filemtime($file) > filemtime($thumb_file)) {
  875. $this->makeThumb($file);
  876. }
  877. $smallThumb =
  878. ($size[0] <= $this->config['thumbWidth']) &&
  879. ($size[1] <= $this->config['thumbHeight']) &&
  880. in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_JPEG));
  881. }
  882. }
  883. }
  884. $stat = stat($file);
  885. if ($stat === false) {
  886. continue;
  887. }
  888. $name = basename($file);
  889. $types = $this->config['types'];
  890. $types = explode(' ', $types['images'] . ' ' . $types['image']);
  891. if (substr($name, 0, 1) == '.' && !$this->config['showHiddenFiles']) {
  892. continue;
  893. }
  894. if ($this->type == 'images' && !in_array(strtolower($ext), $types)) {
  895. continue;
  896. }
  897. $bigIcon = file_exists("themes/{$this->config['theme']}/img/files/big/$ext.png");
  898. $smallIcon = file_exists("themes/{$this->config['theme']}/img/files/small/$ext.png");
  899. $thumb = file_exists("$thumbDir/$name");
  900. $return[] = array(
  901. 'name' => stripcslashes($name),
  902. 'size' => $stat['size'],
  903. 'mtime' => $stat['mtime'],
  904. 'date' => @strftime($this->dateTimeSmall, $stat['mtime']),
  905. 'readable' => is_readable($file),
  906. 'writable' => file::isWritable($file),
  907. 'bigIcon' => $bigIcon,
  908. 'smallIcon' => $smallIcon,
  909. 'thumb' => $thumb,
  910. 'smallThumb' => $smallThumb,
  911. 'preview' => $preview
  912. );
  913. }
  914. return $return;
  915. }
  916. /**
  917. * @param $dir
  918. * @param int $index
  919. * @return array|bool
  920. */
  921. protected function getTree($dir, $index = 0)
  922. {
  923. $path = explode("/", $dir);
  924. $pdir = "";
  925. for ($i = 0; ($i <= $index && $i < count($path)); $i++) {
  926. $pdir .= "/{$path[$i]}";
  927. }
  928. if (strlen($pdir)) {
  929. $pdir = substr($pdir, 1);
  930. }
  931. $fdir = "{$this->config['uploadDir']}/$pdir";
  932. $dirs = $this->getDirs($fdir);
  933. if (is_array($dirs) && count($dirs) && ($index <= count($path) - 1)) {
  934. foreach ($dirs as $i => $cdir) {
  935. if ($cdir['hasDirs'] &&
  936. (
  937. ($index == count($path) - 1) ||
  938. ($cdir['name'] == $path[$index + 1])
  939. )
  940. ) {
  941. $dirs[$i]['dirs'] = $this->getTree($dir, $index + 1);
  942. if (!is_array($dirs[$i]['dirs']) || !count($dirs[$i]['dirs'])) {
  943. unset($dirs[$i]['dirs']);
  944. continue;
  945. }
  946. }
  947. }
  948. } else {
  949. return false;
  950. }
  951. return $dirs;
  952. }
  953. /**
  954. * @param bool $existent
  955. * @return string
  956. */
  957. protected function postDir($existent = true)
  958. {
  959. $dir = $this->typeDir;
  960. if (isset($this->post['dir'])) {
  961. $dir .= "/" . $this->post['dir'];
  962. }
  963. if ($existent && (!is_dir($dir) || !is_readable($dir))) {
  964. $this->errorMsg("Inexistant or inaccessible folder.");
  965. }
  966. return $dir;
  967. }
  968. /**
  969. * @param bool $existent
  970. * @return string
  971. */
  972. protected function getDir($existent = true)
  973. {
  974. $dir = $this->typeDir;
  975. if (isset($this->get['dir'])) {
  976. $dir .= "/" . $this->get['dir'];
  977. }
  978. if ($existent && (!is_dir($dir) || !is_readable($dir))) {
  979. $this->errorMsg("Inexistant or inaccessible folder.");
  980. }
  981. return $dir;
  982. }
  983. /**
  984. * @param $dir
  985. * @return array
  986. */
  987. protected function getDirs($dir)
  988. {
  989. $dirs = dir::content($dir, array('types' => "dir"));
  990. $return = array();
  991. if (is_array($dirs)) {
  992. $writable = dir::isWritable($dir);
  993. foreach ($dirs as $cdir) {
  994. $info = $this->getDirInfo($cdir);
  995. if ($info === false) {
  996. continue;
  997. }
  998. $info['removable'] = $writable && $info['writable'];
  999. $return[] = $info;
  1000. }
  1001. }
  1002. return $return;
  1003. }
  1004. /**
  1005. * @param $dir
  1006. * @param bool $removable
  1007. * @return array|bool
  1008. */
  1009. protected function getDirInfo($dir, $removable = false)
  1010. {
  1011. if ((substr(basename($dir), 0, 1) == ".") || !is_dir($dir) || !is_readable($dir)) {
  1012. return false;
  1013. }
  1014. $dirs = dir::content($dir, array('types' => "dir"));
  1015. if (is_array($dirs)) {
  1016. foreach ($dirs as $key => $cdir) {
  1017. if (substr(basename($cdir), 0, 1) == ".") {
  1018. unset($dirs[$key]);
  1019. }
  1020. }
  1021. $hasDirs = count($dirs) ? true : false;
  1022. } else {
  1023. $hasDirs = false;
  1024. }
  1025. $writable = dir::isWritable($dir);
  1026. $info = array(
  1027. 'name' => stripslashes(basename($dir)),
  1028. 'readable' => is_readable($dir),
  1029. 'writable' => $writable,
  1030. 'removable' => $removable && $writable && dir::isWritable(dirname($dir)),
  1031. 'hasDirs' => $hasDirs
  1032. );
  1033. if ($dir == "{$this->config['uploadDir']}/{$this->session['dir']}") {
  1034. $info['current'] = true;
  1035. }
  1036. return $info;
  1037. }
  1038. /**
  1039. * @param null $data
  1040. * @param null $template
  1041. * @return string
  1042. */
  1043. protected function output($data = null, $template = null)
  1044. {
  1045. if (!is_array($data)) {
  1046. $data = array();
  1047. }
  1048. if ($template === null) {
  1049. $template = $this->action;
  1050. }
  1051. if (file_exists("tpl/tpl_$template.php")) {
  1052. ob_start();
  1053. $eval = "unset(\$data);unset(\$template);unset(\$eval);";
  1054. $_ = $data;
  1055. foreach (array_keys($data) as $key) {
  1056. if (preg_match('/^[a-z\d_]+$/i', $key)) {
  1057. $eval .= "\$$key=\$_['$key'];";
  1058. }
  1059. }
  1060. $eval .= "unset(\$_);require \"tpl/tpl_$template.php\";";
  1061. eval($eval);
  1062. return ob_get_clean();
  1063. }
  1064. return "";
  1065. }
  1066. /**
  1067. * @param $message
  1068. * @param array|null $data
  1069. */
  1070. protected function errorMsg($message, array $data = null)
  1071. {
  1072. if (in_array($this->action, array("thumb", "upload", "download", "downloadDir"))) {
  1073. die($this->label($message, $data));
  1074. }
  1075. if (($this->action === null) || ($this->action == "browser")) {
  1076. $this->backMsg($message, $data);
  1077. } else {
  1078. $message = $this->label($message, $data);
  1079. die(json_encode(array('error' => $message)));
  1080. }
  1081. }
  1082. }