<?php
/*
 * jQuery File Upload Plugin PHP Example 5.2.7
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://creativecommons.org/licenses/MIT/
 */

// array_replace_recursive exist in php >= 5.3.0
if (!function_exists('array_replace_recursive')) {
	function array_replace_recursive($array, $array1) {
		function recurse($array, $array1) {
			foreach ($array1 as $key => $value) {
				// create new key in $array, if it is empty or not an array
				if (!isset($array[ $key ]) || (isset($array[ $key ]) && !is_array($array[ $key ]))) {
					$array[ $key ] = array();
				}

				// overwrite the value in the base array
				if (is_array($value)) {
					$value = recurse($array[ $key ], $value);
				}
				$array[ $key ] = $value;
			}
			return $array;
		}

		// handle the arguments, merge one by one
		$args = func_get_args();
		$array = $args[ 0 ];
		if (!is_array($array)) {
			return $array;
		}
		for ($i = 1; $i < count($args); $i++) {
			if (is_array($args[ $i ])) {
				$array = recurse($array, $args[ $i ]);
			}
		}
		return $array;
	}
}


class ResourceUploadHandler {
	private $options;

	function __construct($options = null) {
		$this->options = array(
			'script_url' => $_SERVER[ 'PHP_SELF' ],
			'upload_dir' => dirname(__FILE__) . '/files/',
			'upload_url' => dirname($_SERVER[ 'PHP_SELF' ]) . '/files/',
			'param_name' => 'files',
			// The php.ini settings upload_max_filesize and post_max_size
			// take precedence over the following max_file_size setting:
			'max_file_size' => null,
			'min_file_size' => 1,
			'accept_file_types' => '/.+$/i',
			'max_number_of_files' => null,
			'discard_aborted_uploads' => true,
			'image_versions' => array(
				// Uncomment the following version to restrict the size of
				// uploaded images. You can also add additional versions with
				// their own upload directories:
				/*
								'large' => array(
									'upload_dir' => dirname(__FILE__).'/files/',
									'upload_url' => dirname($_SERVER['PHP_SELF']).'/files/',
									'max_width' => 1920,
									'max_height' => 1200
								),
								*/
				'thumbnail' => array(
					'upload_dir' => dirname(__FILE__) . '/thumbnails/',
					'upload_url' => dirname($_SERVER[ 'PHP_SELF' ]) . '/thumbnails/',
					'max_width' => 80,
					'max_height' => 80
				)
			)
		);
		if ($options) {
			$this->options = array_replace_recursive($this->options, $options);
		}
	}

	private function get_file_object($file_name) {
		$file_path = $this->options[ 'upload_dir' ] . $file_name;
		if (is_file($file_path) && $file_name[ 0 ] !== '.') {
			$file = new stdClass();
			$file->name = $file_name;
			$file->size = filesize($file_path);
			$file->url = $this->options[ 'upload_url' ] . rawurlencode($file->name);
			foreach ($this->options[ 'image_versions' ] as $version => $options) {
				if (is_file($options[ 'upload_dir' ] . $file_name)) {
					$file->{$version . '_url'} = $options[ 'upload_url' ]
							. rawurlencode($file->name);
				}
			}
			$file->delete_url = $this->options[ 'script_url' ]
					. '?file=' . rawurlencode($file->name);
			$file->delete_type = 'DELETE';
			return $file;
		}
		return null;
	}

	private function get_file_objects() {
		return array_values(array_filter(array_map(
			array( $this, 'get_file_object' ),
			scandir($this->options[ 'upload_dir' ])
		)));
	}

	private function create_scaled_image($file_name, $options) {
		$file_path = $this->options[ 'upload_dir' ] . $file_name;
		$new_file_path = $options[ 'upload_dir' ] . $file_name;
		list($img_width, $img_height) = @getimagesize($file_path);
		if (!$img_width || !$img_height) {
			return false;
		}
		$scale = min(
			$options[ 'max_width' ] / $img_width,
				$options[ 'max_height' ] / $img_height
		);
		if ($scale > 1) {
			$scale = 1;
		}
		$new_width = $img_width * $scale;
		$new_height = $img_height * $scale;
		$new_img = @imagecreatetruecolor($new_width, $new_height);
		switch (strtolower(substr(strrchr($file_name, '.'), 1))) {
			case 'jpg':
			case 'jpeg':
				$src_img = @imagecreatefromjpeg($file_path);
				$write_image = 'imagejpeg';
				break;
			case 'gif':
				@imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
				$src_img = @imagecreatefromgif($file_path);
				$write_image = 'imagegif';
				break;
			case 'png':
				@imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
				@imagealphablending($new_img, false);
				@imagesavealpha($new_img, true);
				$src_img = @imagecreatefrompng($file_path);
				$write_image = 'imagepng';
				break;
			default:
				$src_img = $image_method = null;
		}
		$success = $src_img && @imagecopyresampled(
			$new_img,
			$src_img,
			0, 0, 0, 0,
			$new_width,
			$new_height,
			$img_width,
			$img_height
		) && $write_image($new_img, $new_file_path);
		// Free up memory (imagedestroy does not delete files):
		@imagedestroy($src_img);
		@imagedestroy($new_img);
		return $success;
	}

	private function has_error($uploaded_file, $file, $error) {
		if ($error) {
			return $error;
		}
		if (!preg_match($this->options[ 'accept_file_types' ], $file->name)) {
			return 'acceptFileTypes';
		}
		if ($uploaded_file && is_uploaded_file($uploaded_file)) {
			$file_size = filesize($uploaded_file);
		} else {
			$file_size = $_SERVER[ 'CONTENT_LENGTH' ];
		}

		if ($this->options[ 'max_file_size' ] && (
				$file_size > $this->options[ 'max_file_size' ] ||
						$file->size > $this->options[ 'max_file_size' ])
		) {
			return 'maxFileSize';
		}
		if ($this->options[ 'min_file_size' ] &&
				$file_size < $this->options[ 'min_file_size' ]
		) {
			return 'minFileSize';
		}
		if (is_int($this->options[ 'max_number_of_files' ]) && (
				count($this->get_file_objects()) >= $this->options[ 'max_number_of_files' ])
		) {
			return 'maxNumberOfFiles';
		}
		return $error;
	}

	private function handle_file_upload($uploaded_file, $name, $size, $type, $error) {
		$file = new stdClass();
		// Remove path information and dots around the filename, to prevent uploading
		// into different directories or replacing hidden system files.
		// Also remove control characters and spaces (\x00..\x20) around the filename:
		$name = $name == '' ? 'UnknownFile' : $name;
		$name = str_replace(" ", "_", stripslashes($name));
		// basename removes first part of filename like тест_архив.zip (with non-latin characters). Basename of that name will be _архив.zip
		if ($this->strpos_array($name, array( '/', '\'' )) !== false) {
			$name = basename($name);
		} else {
			$res = strrpos($name, '/');
			if ($res !== false) {
				$name = substr($name, $res + 1);
			}
			$res = strrpos($name, '\'');
			if ($res !== false) {
				$name = substr($name, $res + 1);
			}
		}
		$file->name = trim($name, ".\x00..\x20");

		$file->size = intval($size);
		$file->type = $type;

		// error check
        if($error){
		    $error = getTextUploadError($error);
        }
		$error = $this->has_error($uploaded_file, $file, $error);
		if (!$error && $file->name) {
			if (!is_dir(DIR_RESOURCE . $this->options[ 'upload_dir' ])) {
				$path = '';
				$directories = explode('/', str_replace('../', '', $this->options[ 'upload_dir' ]));
				foreach ($directories as $directory) {
					$path = $path . '/' . $directory;
					if (!is_dir(DIR_RESOURCE . $path)) {
						@mkdir(DIR_RESOURCE . $path, 0777);
						chmod(DIR_RESOURCE . $path, 0777);
					}
				}
			}

			if (!is_dir(DIR_RESOURCE . $this->options[ 'upload_dir' ]) || !is_writeable(DIR_RESOURCE . $this->options[ 'upload_dir' ])) {
				$error = "Please check 'resources' folder permissions.";
			}
		}
		if (!$error && $file->name) {
			$file_path = DIR_RESOURCE . $this->options[ 'upload_dir' ] . $file->name;
			$append_file = !$this->options[ 'discard_aborted_uploads' ] &&
					is_file($file_path) && $file->size > filesize($file_path);
			clearstatcache();
			if ($uploaded_file && is_uploaded_file($uploaded_file)) {
				// multipart/formdata uploads (POST method uploads)
				if ($append_file) {
					file_put_contents(
						$file_path,
						fopen($uploaded_file, 'r'),
						FILE_APPEND
					);
				} else {
					$result = move_uploaded_file($uploaded_file, $file_path);
                    if($result===false){
                        $file->error = 'Failed! Check error log for details.';
                        $error = new AError('Error! Tryed to move uploaded file from '.$uploaded_file.' to '.$file_path);
                        $error->toLog();
                    }
				}
			} else {
				// Non-multipart uploads (PUT method support)
				file_put_contents(
					$file_path,
					fopen('php://input', 'r'),
					$append_file ? FILE_APPEND : 0
				);
			}
			$file_size = filesize($file_path);
			if ($file_size === $file->size) {
				$file->url = $this->options[ 'upload_url' ] . rawurlencode($file->name);
				chmod($file_path, 0777);
			} else if ($this->options[ 'discard_aborted_uploads' ]) {
				unlink($file_path);
				$file->error = 'Failed! Check error log for details.';
			}
			$file->size = $file_size;
			$file->delete_url = $this->options[ 'script_url' ] . '&resource_id=%ID%';
			$file->delete_type = 'DELETE';
		} else {
			$file->error = $error;
		}
		return $file;
	}

	public function get() {
		$file_name = isset($_REQUEST[ 'file' ]) ?
				basename(stripslashes($_REQUEST[ 'file' ])) : null;
		if ($file_name) {
			$info = $this->get_file_object($file_name);
		} else {
			$info = $this->get_file_objects();
		}

		return $info;
	}

	public function post() {

		$upload = isset($_FILES[ $this->options[ 'param_name' ] ]) ? $_FILES[ $this->options[ 'param_name' ] ] : array(
																														'tmp_name' => null,
																														'name' => null,
																														'size' => null,
																														'type' => null,
																														'error' => 'emptyResult'
																													);
		$info = array();
		if (is_array($upload[ 'tmp_name' ])) {
			foreach ($upload[ 'tmp_name' ] as $index => $value) {
				$info[ ] = $this->handle_file_upload(
					$upload[ 'tmp_name' ][ $index ],
					isset($_SERVER[ 'HTTP_X_FILE_NAME' ]) ?
							$_SERVER[ 'HTTP_X_FILE_NAME' ] : $upload[ 'name' ][ $index ],
					isset($_SERVER[ 'HTTP_X_FILE_SIZE' ]) ?
							$_SERVER[ 'HTTP_X_FILE_SIZE' ] : $upload[ 'size' ][ $index ],
					isset($_SERVER[ 'HTTP_X_FILE_TYPE' ]) ?
							$_SERVER[ 'HTTP_X_FILE_TYPE' ] : $upload[ 'type' ][ $index ],
					$upload[ 'error' ][ $index ]
				);
			}
		} else {
			$info[ ] = $this->handle_file_upload(
				$upload[ 'tmp_name' ],
				isset($_SERVER[ 'HTTP_X_FILE_NAME' ]) ?
						$_SERVER[ 'HTTP_X_FILE_NAME' ] : $upload[ 'name' ],
				isset($_SERVER[ 'HTTP_X_FILE_SIZE' ]) ?
						$_SERVER[ 'HTTP_X_FILE_SIZE' ] : $upload[ 'size' ],
				isset($_SERVER[ 'HTTP_X_FILE_TYPE' ]) ?
						$_SERVER[ 'HTTP_X_FILE_TYPE' ] : $upload[ 'type' ],
				$upload[ 'error' ]
			);
		}

		return $info;
	}

	public function delete() {
		$file_name = isset($_REQUEST[ 'file' ]) ?
				basename(stripslashes($_REQUEST[ 'file' ])) : null;
		$file_path = $this->options[ 'upload_dir' ] . $file_name;
		$success = is_file($file_path) && $file_name[ 0 ] !== '.' && unlink($file_path);

		return $success;
	}

	public function strpos_array($haystack, $needle) {
		if (!is_array($needle)) {
			$needle = array( $needle );
		}
		foreach ($needle as $what) {
			if (($pos = strpos($haystack, $what)) !== false) {
				return $pos;
			}
		}
		return false;
	}

}
