<?php

/**
 * Default elFinder connector
 *
 * @author Dmitry (dio) Levashov
 **/
class elFinderConnector {
	/**
	 * elFinder instance
	 *
	 * @var elFinder
	 **/
	protected $elFinder;
	
	/**
	 * Options
	 *
	 * @var aray
	 **/
	protected $options = array();
	
	/**
	 * undocumented class variable
	 *
	 * @var string
	 **/
	protected $header = 'Content-Type: application/json';

	/**
	 * HTTP request method
	 * 
	 * @var string
	 */
	protected $reqMethod = '';
	
	/**
	 * Constructor
	 *
	 * @param $elFinder
	 * @param bool $debug
	 * @author Dmitry (dio) Levashov
	 */
	public function __construct($elFinder, $debug=false) {
		
		$this->elFinder = $elFinder;
		$this->reqMethod = strtoupper($_SERVER["REQUEST_METHOD"]);
		if ($debug) {
			$this->header = 'Content-Type: text/html; charset=utf-8';
		}
	}
	
	/**
	 * Execute elFinder command and output result
	 *
	 * @return void
	 * @author Dmitry (dio) Levashov
	 **/
	public function run() {
		$isPost = $this->reqMethod === 'POST';
		$src    = $isPost ? $_POST : $_GET;
		if ($isPost && !$src && $rawPostData = @file_get_contents('php://input')) {
			// for support IE XDomainRequest()
			$parts = explode('&', $rawPostData);
			foreach($parts as $part) {
				list($key, $value) = array_pad(explode('=', $part), 2, '');
				$key = rawurldecode($key);
				if (substr($key, -2) === '[]') {
					$key = substr($key, 0, strlen($key) - 2);
					if (!isset($src[$key])) {
						$src[$key] = array();
					}
					$src[$key][] = rawurldecode($value);
				} else {
					$src[$key] = rawurldecode($value);
				}
			}
			$_POST = $this->input_filter($src);
			$_REQUEST = $this->input_filter(array_merge_recursive($src, $_REQUEST));
		}
		$cmd    = isset($src['cmd']) ? $src['cmd'] : '';
		$args   = array();
		
		if (!function_exists('json_encode')) {
			$error = $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_JSON);
			$this->output(array('error' => '{"error":["'.implode('","', $error).'"]}', 'raw' => true));
		}
		
		if (!$this->elFinder->loaded()) {
			$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_VOL), 'debug' => $this->elFinder->mountErrors));
		}
		
		// telepat_mode: on
		if (!$cmd && $isPost) {
			$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UPLOAD, elFinder::ERROR_UPLOAD_TOTAL_SIZE), 'header' => 'Content-Type: text/html'));
		}
		// telepat_mode: off
		
		if (!$this->elFinder->commandExists($cmd)) {
			$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD)));
		}
		
		// collect required arguments to exec command
		$hasFiles = false;
		foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) {
			if ($name === 'FILES') {
				if (isset($_FILES)) {
					$hasFiles = true;
				} elseif ($req) {
					$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd)));
				}
			} else {
				$arg = isset($src[$name])? $src[$name] : '';
			
				if (!is_array($arg) && $req !== '') {
					$arg = trim($arg);
				}
				if ($req && $arg === '') {
					$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd)));
				}
				$args[$name] = $arg;
			}
		}
		
		$args['debug'] = isset($src['debug']) ? !!$src['debug'] : false;
		
		$args = $this->input_filter($args);
		if ($hasFiles) {
			$args['FILES'] = $_FILES;
		}
		
		$this->output($this->elFinder->exec($cmd, $args));
	}
	
	/**
	 * Output json
	 *
	 * @param  array  data to output
	 * @return void
	 * @author Dmitry (dio) Levashov
	 **/
	protected function output(array $data) {
		// clear output buffer
		while(ob_get_level() && ob_end_clean()){}
		
		$header = isset($data['header']) ? $data['header'] : $this->header;
		unset($data['header']);
		if ($header) {
			if (is_array($header)) {
				foreach ($header as $h) {
					header($h);
				}
			} else {
				header($header);
			}
		}
		
		if (isset($data['pointer'])) {
			$toEnd = true;
			$fp = $data['pointer'];
			if (($this->reqMethod === 'GET' || $this->reqMethod === 'HEAD')
					&& elFinder::isSeekableStream($fp)
					&& (array_search('Accept-Ranges: none', headers_list()) === false)) {
				header('Accept-Ranges: bytes');
				$psize = null;
				if (!empty($_SERVER['HTTP_RANGE'])) {
					$size = $data['info']['size'];
					$start = 0;
					$end = $size - 1;
					if (preg_match('/bytes=(\d*)-(\d*)(,?)/i', $_SERVER['HTTP_RANGE'], $matches)) {
						if (empty($matches[3])) {
							if (empty($matches[1]) && $matches[1] !== '0') {
								$start = $size - $matches[2];
							} else {
								$start = intval($matches[1]);
								if (!empty($matches[2])) {
									$end = intval($matches[2]);
									if ($end >= $size) {
										$end = $size - 1;
									}
									$toEnd = ($end == ($size - 1));
								}
							}
							$psize = $end - $start + 1;
							
							header('HTTP/1.1 206 Partial Content');
							header('Content-Length: ' . $psize);
							header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
							
							fseek($fp, $start);
						}
					}
				}
				if (is_null($psize)){
					elFinder::rewind($fp);
				}
			} else {
				header('Accept-Ranges: none');
				if (isset($data['info']) && ! $data['info']['size']) {
					if (function_exists('header_remove')) {
						header_remove('Content-Length');
					} else {
						header('Content-Length:');
					}
				}
			}

			// unlock session data for multiple access
			$this->elFinder->getSession()->close();
			// client disconnect should abort
			ignore_user_abort(false);

			if ($reqMethod !== 'HEAD') {
				if ($toEnd) {
					fpassthru($fp);
				} else {
					$out = fopen('php://output', 'wb');
					stream_copy_to_stream($fp, $out, $psize);
					fclose($out);
				}
			}
			
			if (!empty($data['volume'])) {
				$data['volume']->close($data['pointer'], $data['info']['hash']);
			}
			exit();
		} else {
			if (!empty($data['raw']) && !empty($data['error'])) {
				echo $data['error'];
			} else {
				if (isset($data['debug']) && isset($data['debug']['phpErrors'])) {
					$data['debug']['phpErrors'] = array_merge($data['debug']['phpErrors'], elFinder::$phpErrors);
				}
				echo json_encode($data);
			}
			flush();
			exit(0);
		}
		
	}
	
	/**
	 * Remove null & stripslashes applies on "magic_quotes_gpc"
	 * 
	 * @param  mixed  $args
	 * @return mixed
	 * @author Naoki Sawada
	 */
	protected function input_filter($args) {
		static $magic_quotes_gpc = NULL;
		
		if ($magic_quotes_gpc === NULL)
			$magic_quotes_gpc = (version_compare(PHP_VERSION, '5.4', '<') && get_magic_quotes_gpc());
		
		if (is_array($args)) {
			return array_map(array(& $this, 'input_filter'), $args);
		}
		$res = str_replace("\0", '', $args);
		$magic_quotes_gpc && ($res = stripslashes($res));
		return $res;
	}
}// END class 
