elFinderConnector.class.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <?php
  2. /**
  3. * Default elFinder connector
  4. *
  5. * @author Dmitry (dio) Levashov
  6. **/
  7. class elFinderConnector {
  8. /**
  9. * elFinder instance
  10. *
  11. * @var elFinder
  12. **/
  13. protected $elFinder;
  14. /**
  15. * Options
  16. *
  17. * @var aray
  18. **/
  19. protected $options = array();
  20. /**
  21. * Must be use output($data) $data['header']
  22. *
  23. * @var string
  24. * @deprecated
  25. **/
  26. protected $header = '';
  27. /**
  28. * HTTP request method
  29. *
  30. * @var string
  31. */
  32. protected $reqMethod = '';
  33. /**
  34. * Content type of output JSON
  35. *
  36. * @var string
  37. */
  38. protected static $contentType = 'Content-Type: application/json; charset=utf-8';
  39. /**
  40. * Constructor
  41. *
  42. * @param $elFinder
  43. * @param bool $debug
  44. * @author Dmitry (dio) Levashov
  45. */
  46. public function __construct($elFinder, $debug=false) {
  47. $this->elFinder = $elFinder;
  48. $this->reqMethod = strtoupper($_SERVER["REQUEST_METHOD"]);
  49. if ($debug) {
  50. self::$contentType = 'Content-Type: text/plain; charset=utf-8';
  51. }
  52. }
  53. /**
  54. * Execute elFinder command and output result
  55. *
  56. * @return void
  57. * @author Dmitry (dio) Levashov
  58. **/
  59. public function run() {
  60. $isPost = $this->reqMethod === 'POST';
  61. $src = $isPost ? array_merge($_GET, $_POST) : $_GET;
  62. $maxInputVars = (! $src || isset($src['targets']))? ini_get('max_input_vars') : null;
  63. if ((! $src || $maxInputVars) && $rawPostData = file_get_contents('php://input')) {
  64. // for max_input_vars and supports IE XDomainRequest()
  65. $parts = explode('&', $rawPostData);
  66. if (! $src || $maxInputVars < count($parts)) {
  67. $src = array();
  68. foreach($parts as $part) {
  69. list($key, $value) = array_pad(explode('=', $part), 2, '');
  70. $key = rawurldecode($key);
  71. if (preg_match('/^(.+?)\[([^\[\]]*)\]$/', $key, $m)) {
  72. $key = $m[1];
  73. $idx = $m[2];
  74. if (!isset($src[$key])) {
  75. $src[$key] = array();
  76. }
  77. if ($idx) {
  78. $src[$key][$idx] = rawurldecode($value);
  79. } else {
  80. $src[$key][] = rawurldecode($value);
  81. }
  82. } else {
  83. $src[$key] = rawurldecode($value);
  84. }
  85. }
  86. $_POST = $this->input_filter($src);
  87. $_REQUEST = $this->input_filter(array_merge_recursive($src, $_REQUEST));
  88. }
  89. }
  90. if (isset($src['targets']) && $this->elFinder->maxTargets && count($src['targets']) > $this->elFinder->maxTargets) {
  91. $error = $this->elFinder->error(elFinder::ERROR_MAX_TARGTES);
  92. $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_MAX_TARGTES)));
  93. }
  94. $cmd = isset($src['cmd']) ? $src['cmd'] : '';
  95. $args = array();
  96. if (!function_exists('json_encode')) {
  97. $error = $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_JSON);
  98. $this->output(array('error' => '{"error":["'.implode('","', $error).'"]}', 'raw' => true));
  99. }
  100. if (!$this->elFinder->loaded()) {
  101. $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_VOL), 'debug' => $this->elFinder->mountErrors));
  102. }
  103. // telepat_mode: on
  104. if (!$cmd && $isPost) {
  105. $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UPLOAD, elFinder::ERROR_UPLOAD_TOTAL_SIZE), 'header' => 'Content-Type: text/html'));
  106. }
  107. // telepat_mode: off
  108. if (!$this->elFinder->commandExists($cmd)) {
  109. $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD)));
  110. }
  111. // collect required arguments to exec command
  112. $hasFiles = false;
  113. foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) {
  114. if ($name === 'FILES') {
  115. if (isset($_FILES)) {
  116. $hasFiles = true;
  117. } elseif ($req) {
  118. $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd)));
  119. }
  120. } else {
  121. $arg = isset($src[$name])? $src[$name] : '';
  122. if (!is_array($arg) && $req !== '') {
  123. $arg = trim($arg);
  124. }
  125. if ($req && $arg === '') {
  126. $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd)));
  127. }
  128. $args[$name] = $arg;
  129. }
  130. }
  131. $args['debug'] = isset($src['debug']) ? !!$src['debug'] : false;
  132. $args = $this->input_filter($args);
  133. if ($hasFiles) {
  134. $args['FILES'] = $_FILES;
  135. }
  136. try {
  137. $this->output($this->elFinder->exec($cmd, $args));
  138. } catch (elFinderAbortException $e) {
  139. // connection aborted
  140. // unlock session data for multiple access
  141. $this->elFinder->getSession()->close();
  142. // HTTP response code
  143. header('HTTP/1.0 204 No Content');
  144. // clear output buffer
  145. while(ob_get_level() && ob_end_clean()){}
  146. exit();
  147. }
  148. }
  149. /**
  150. * Output json
  151. *
  152. * @param array data to output
  153. * @return void
  154. * @author Dmitry (dio) Levashov
  155. **/
  156. protected function output(array $data) {
  157. // unlock session data for multiple access
  158. $this->elFinder->getSession()->close();
  159. // client disconnect should abort
  160. ignore_user_abort(false);
  161. if ($this->header) {
  162. self::sendHeader($this->header);
  163. }
  164. if (isset($data['pointer'])) {
  165. // set time limit to 0
  166. elFinder::extendTimeLimit(0);
  167. // send optional header
  168. if (!empty($data['header'])) {
  169. self::sendHeader($data['header']);
  170. }
  171. // clear output buffer
  172. while(ob_get_level() && ob_end_clean()){}
  173. $toEnd = true;
  174. $fp = $data['pointer'];
  175. $sendData = !($this->reqMethod === 'HEAD' || !empty($data['info']['xsendfile']));
  176. if (($this->reqMethod === 'GET' || !$sendData)
  177. && elFinder::isSeekableStream($fp)
  178. && (array_search('Accept-Ranges: none', headers_list()) === false)) {
  179. header('Accept-Ranges: bytes');
  180. $psize = null;
  181. if (!empty($_SERVER['HTTP_RANGE'])) {
  182. $size = $data['info']['size'];
  183. $start = 0;
  184. $end = $size - 1;
  185. if (preg_match('/bytes=(\d*)-(\d*)(,?)/i', $_SERVER['HTTP_RANGE'], $matches)) {
  186. if (empty($matches[3])) {
  187. if (empty($matches[1]) && $matches[1] !== '0') {
  188. $start = $size - $matches[2];
  189. } else {
  190. $start = intval($matches[1]);
  191. if (!empty($matches[2])) {
  192. $end = intval($matches[2]);
  193. if ($end >= $size) {
  194. $end = $size - 1;
  195. }
  196. $toEnd = ($end == ($size - 1));
  197. }
  198. }
  199. $psize = $end - $start + 1;
  200. header('HTTP/1.1 206 Partial Content');
  201. header('Content-Length: ' . $psize);
  202. header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
  203. // Apache mod_xsendfile dose not support range request
  204. if (isset($data['info']['xsendfile']) && strtolower($data['info']['xsendfile']) === 'x-sendfile') {
  205. if (function_exists('header_remove')) {
  206. header_remove($data['info']['xsendfile']);
  207. } else {
  208. header($data['info']['xsendfile'] . ':');
  209. }
  210. unset($data['info']['xsendfile']);
  211. if ($this->reqMethod !== 'HEAD') {
  212. $sendData = true;
  213. }
  214. }
  215. $sendData && fseek($fp, $start);
  216. }
  217. }
  218. }
  219. if ($sendData && is_null($psize)){
  220. elFinder::rewind($fp);
  221. }
  222. } else {
  223. header('Accept-Ranges: none');
  224. if (isset($data['info']) && ! $data['info']['size']) {
  225. if (function_exists('header_remove')) {
  226. header_remove('Content-Length');
  227. } else {
  228. header('Content-Length:');
  229. }
  230. }
  231. }
  232. if ($sendData) {
  233. if ($toEnd) {
  234. // PHP < 5.6 has a bug of fpassthru
  235. // see https://bugs.php.net/bug.php?id=66736
  236. if (version_compare(PHP_VERSION, '5.6', '<')) {
  237. file_put_contents('php://output', $fp);
  238. } else {
  239. fpassthru($fp);
  240. }
  241. } else {
  242. $out = fopen('php://output', 'wb');
  243. stream_copy_to_stream($fp, $out, $psize);
  244. fclose($out);
  245. }
  246. }
  247. if (!empty($data['volume'])) {
  248. $data['volume']->close($data['pointer'], $data['info']['hash']);
  249. }
  250. exit();
  251. } else {
  252. self::outputJson($data);
  253. exit(0);
  254. }
  255. }
  256. /**
  257. * Remove null & stripslashes applies on "magic_quotes_gpc"
  258. *
  259. * @param mixed $args
  260. * @return mixed
  261. * @author Naoki Sawada
  262. */
  263. protected function input_filter($args) {
  264. static $magic_quotes_gpc = NULL;
  265. if ($magic_quotes_gpc === NULL)
  266. $magic_quotes_gpc = (version_compare(PHP_VERSION, '5.4', '<') && get_magic_quotes_gpc());
  267. if (is_array($args)) {
  268. return array_map(array(& $this, 'input_filter'), $args);
  269. }
  270. $res = str_replace("\0", '', $args);
  271. $magic_quotes_gpc && ($res = stripslashes($res));
  272. return $res;
  273. }
  274. /**
  275. * Send HTTP header
  276. *
  277. * @param string|array $header optional header
  278. */
  279. protected static function sendHeader($header = null) {
  280. if ($header) {
  281. if (is_array($header)) {
  282. foreach ($header as $h) {
  283. header($h);
  284. }
  285. } else {
  286. header($header);
  287. }
  288. }
  289. }
  290. /**
  291. * Output JSON
  292. *
  293. * @param array $data
  294. */
  295. public static function outputJson($data) {
  296. // send header
  297. $header = isset($data['header']) ? $data['header'] : self::$contentType;
  298. self::sendHeader($header);
  299. unset($data['header']);
  300. if (!empty($data['raw']) && isset($data['error'])) {
  301. $out = $data['error'];
  302. } else {
  303. if (isset($data['debug']) && isset($data['debug']['phpErrors'])) {
  304. $data['debug']['phpErrors'] = array_merge($data['debug']['phpErrors'], elFinder::$phpErrors);
  305. }
  306. $out = json_encode($data);
  307. }
  308. // clear output buffer
  309. while(ob_get_level() && ob_end_clean()){}
  310. header('Content-Length: ' . strlen($out));
  311. echo $out;
  312. flush();
  313. }
  314. }// END class