Index: branches/5.2.x/core/kernel/utility/formatters/upload_formatter.php
===================================================================
--- branches/5.2.x/core/kernel/utility/formatters/upload_formatter.php	(revision 16686)
+++ branches/5.2.x/core/kernel/utility/formatters/upload_formatter.php	(revision 16687)
@@ -1,658 +1,657 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 defined('FULL_PATH') or die('restricted access!');
 
 class kUploadFormatter extends kFormatter
 {
 	var $DestinationPath;
 
 	var $FullPath;
 
 	/**
 	 * File helper reference
 	 *
 	 * @var FileHelper
 	 */
 	var $fileHelper = NULL;
 
 	/**
 	 * Uploaded files, that are ordered as required (both live & temp images in one list)
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $sorting = Array ();
 
 	/**
 	 * Creates formatter instance
 	 *
 	 * @access public
 	 */
 	public function __construct()
 	{
 		parent::__construct();
 
 		$this->fileHelper = $this->Application->recallObject('FileHelper');
 
 		if ( $this->DestinationPath ) {
 			$this->FullPath = FULL_PATH . $this->DestinationPath;
 		}
 	}
 
 	/**
 	 * Sets field option defaults.
 	 *
 	 * @param string  $field_name    Field nae.
 	 * @param array   $field_options Field options.
 	 * @param kDBBase $object        Object.
 	 *
 	 * @return void
 	 */
 	public function PrepareOptions($field_name, &$field_options, &$object)
 	{
 		if ( !$this->DestinationPath && !isset($field_options['upload_dir']) ) {
 			$base_path = $this->Application->getUnitOption($object->Prefix, 'BasePath');
 			$field_options['upload_dir'] = WRITEBALE_BASE . '/' . basename($base_path) . '/';
 		}
 
 		if ( !isset($field_options['max_size']) ) {
 			$field_options['max_size'] = MAX_UPLOAD_SIZE;
 		}
 	}
 
 	/**
 	 * Processes file uploads from form
 	 *
 	 * @param mixed $value
 	 * @param string $field_name
 	 * @param kDBItem $object
 	 * @return mixed
 	 * @access public
 	 */
 	public function Parse($value, $field_name, &$object)
 	{
 		$value = $this->Application->HttpQuery->unescapeRequestVariable($value);
 		$options = $object->GetFieldOptions($field_name);
 
 		if ( getArrayValue($options, 'upload_dir') ) {
 			$this->DestinationPath = $options['upload_dir'];
 			$this->FullPath = FULL_PATH . $this->DestinationPath;
 		}
 
 		if ( is_array($value) && isset($value['tmp_ids']) ) {
 			$ret = $this->_processFlashUploader($value, $field_name, $object);
 		}
 		else {
 			$ret = $this->_processRegularUploader($value, $field_name, $object);
 		}
 
 		if ( getArrayValue($options, 'upload_dir') ) {
 			$this->DestinationPath = null;
 			$this->FullPath = null;
 		}
 
 		return $ret;
 	}
 
 	/**
 	 * Handles uploaded files, provided by Flash uploader
 	 *
 	 * @param Array|string $value
 	 * @param string $field_name
 	 * @param kDBItem $object
 	 * @return string
 	 * @access protected
 	 */
 	protected function _processFlashUploader($value, $field_name, $object)
 	{
 		$options = $object->GetFieldOptions($field_name);
 		$this->sorting = isset($value['order']) ? explode('|', $value['order']) : Array ();
 
 		if ( $value['tmp_deleted'] ) {
 			$n_upload = Array ();
 			$deleted = explode('|', $value['tmp_deleted']);
 			$upload = explode('|', $value['upload']);
 
 			foreach ($upload as $name) {
 				if ( in_array($name, $deleted) ) {
 					continue;
 				}
 
 				$n_upload[] = $name;
 			}
 
 			$value['upload'] = implode('|', $n_upload);
 		}
 
 		if ( !$value['tmp_ids'] ) {
 			// no pending files -> return already uploaded files
 			return $this->_sortFiles($value['upload']);
 		}
 
 		$swf_uploaded_ids = explode('|', $value['tmp_ids']);
 		$swf_uploaded_names = explode('|', $value['tmp_names']);
 		$existing = $value['upload'] ? explode('|', $value['upload']) : Array ();
 
 		$fret = Array ();
 		$max_files = $this->_getMaxFiles($options);
 		$pending_actions = $object->getPendingActions();
 		$files_to_delete = $this->_getFilesToDelete($object);
 
 		for ($i = 0; $i < min($max_files, count($swf_uploaded_ids)); $i++) {
 			// don't delete uploaded file, when it's name matches delete file name
 			$real_name = $this->_getRealFilename($swf_uploaded_names[$i], $options, $object, $files_to_delete);
 			$file_name = $this->FullPath . $real_name;
 
 			$tmp_file = WRITEABLE . '/tmp/' . $swf_uploaded_ids[$i] . '_' . $swf_uploaded_names[$i];
 			rename($tmp_file, $file_name);
 
 			@chmod($file_name, 0666);
 			$fret[] = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name;
 
 			$pending_actions[] = Array (
 				'action' => 'make_live', 'id' => $object->GetID(), 'field' => $field_name, 'file' => $file_name
 			);
 
 			$this->_renameFileInSorting($swf_uploaded_names[$i], $real_name);
 		}
 
 		$object->setPendingActions($pending_actions);
 
 		return $this->_sortFiles(array_merge($existing, $fret));
 	}
 
 	/**
 	 * Returns files, scheduled for deleting
 	 *
 	 * @param kDBItem $object
 	 * @return Array
 	 * @access protected
 	 */
 	protected function _getFilesToDelete($object)
 	{
 		$ret = Array ();
 
 		foreach ($object->getPendingActions() as $data) {
 			if ( $data['action'] == 'delete' ) {
 				$ret[] = $data['file'];
 			}
 		}
 
 		return $ret;
 	}
 
 	/**
 	 * Handles regular file upload
 	 *
 	 * @param string|Array $value
 	 * @param string $field_name
 	 * @param kDBItem $object
 	 * @return string
 	 * @access protected
 	 */
 	protected function _processRegularUploader($value, $field_name, $object)
 	{
 		$ret = !is_array($value) ? $value : '';
 		$options = $object->GetFieldOptions($field_name);
 
 		if ( getArrayValue($value, 'upload') && getArrayValue($value, 'error') == UPLOAD_ERR_NO_FILE ) {
 			// file was not uploaded this time, but was uploaded before, then use previously uploaded file (from db)
 			return getArrayValue($value, 'upload');
 		}
 
 		if ( is_array($value) && count($value) > 1 && $value['size'] ) {
 			if ( is_array($value) && (int)$value['error'] === UPLOAD_ERR_OK ) {
 				// we can get mime type based on file content and don't use one, provided by the client
 //				$value['type'] = kUtil::mimeContentType($value['tmp_name']);
 
-				if ( getArrayValue($options, 'file_types') && !$this->extensionMatch($value['name'], $options['file_types']) ) {
+				if ( getArrayValue($options, 'file_types')
+					&& !$this->fileHelper->extensionMatch($value['name'], $options['file_types'])
+				) {
 					// match by file extensions
 					$error_params = Array (
 						'file_name' => $value['name'],
 						'file_types' => $options['file_types'],
 					);
 
 					$object->SetError($field_name, 'bad_file_format', 'la_error_InvalidFileFormat', $error_params);
 				}
 				elseif ( getArrayValue($options, 'allowed_types') && !in_array($value['type'], $options['allowed_types']) ) {
 					// match by mime type provided by web-browser
 					$error_params = Array (
 						'file_type' => $value['type'],
 						'allowed_types' => $options['allowed_types'],
 					);
 
 					$object->SetError($field_name, 'bad_file_format', 'la_error_InvalidFileFormat', $error_params);
 				}
 				elseif ( $value['size'] > $options['max_size'] ) {
 					$object->SetError($field_name, 'bad_file_size', 'la_error_FileTooLarge');
 				}
 				elseif ( !is_writable($this->FullPath) ) {
 					$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
 				}
 				else {
 					$tmp_path = WRITEABLE . '/tmp/';
 					$filename = $this->fileHelper->ensureUniqueFilename($tmp_path, $value['name'] . '.tmp');
 					$tmp_file_path = $tmp_path . $filename;
 
 					$moved = move_uploaded_file($value['tmp_name'], $tmp_file_path);
 					$storage_format = isset($options['storage_format']) ? $options['storage_format'] : false;
 
 					if ( $storage_format ) {
 						/** @var kUploadHelper $upload_helper */
 						$upload_helper = $this->Application->recallObject('kUploadHelper');
 						$moved = $upload_helper->resizeUploadedFile($tmp_file_path, $storage_format);
 					}
 
 					if ( $moved ) {
 						$real_name = $this->_getRealFilename(
 							kUtil::removeTempExtension(basename($tmp_file_path)),
 							$options,
 							$object
 						);
 						$file_name = $this->FullPath . $real_name;
 						$moved = rename($tmp_file_path, $file_name);
 					}
 
 					if ( !$moved ) {
 						$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
 					}
 					else {
 						@chmod($file_name, 0666);
 
 						if ( getArrayValue($options, 'size_field') ) {
 							$object->SetDBField($options['size_field'], $value['size']);
 						}
 
 						if ( getArrayValue($options, 'orig_name_field') ) {
 							$object->SetDBField($options['orig_name_field'], $value['name']);
 						}
 
 						if ( getArrayValue($options, 'content_type_field') ) {
 							$object->SetDBField($options['content_type_field'], $value['type']);
 						}
 
 						$ret = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath . $real_name;
 
 						// delete previous file, when new file is uploaded under same field
 						/*$previous_file = isset($value['upload']) ? $value['upload'] : false;
 						if ( $previous_file && file_exists($this->FullPath . $previous_file) ) {
 							unlink($this->FullPath . $previous_file);
 						}*/
 					}
 				}
 			}
 			else {
 				$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
 			}
 		}
 
 		if ( (count($value) > 1) && $value['error'] && ($value['error'] != UPLOAD_ERR_NO_FILE) ) {
 			$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file', $value);
 		}
 
 		return $ret;
 	}
 
 	/**
 	 * Checks, that given file name has on of provided file extensions
 	 *
-	 * @param string $filename
-	 * @param string $file_types
-	 * @return bool
-	 * @access protected
+	 * @param string $filename   Filename.
+	 * @param string $file_types File types.
+	 *
+	 * @return     boolean
+	 * @deprecated 5.2.2-B2
+	 * @see        FileHelper::extensionMatch()
 	 */
 	protected function extensionMatch($filename, $file_types)
 	{
-		if ( preg_match_all('/\*\.(.*?)(;|$)/', $file_types, $regs) ) {
-			$file_extension = mb_strtolower(pathinfo($filename, PATHINFO_EXTENSION));
-			$file_extensions = array_map('mb_strtolower', $regs[1]);
-
-			return in_array($file_extension, $file_extensions);
-		}
+		kUtil::deprecatedMethod(__METHOD__, '5.2.2-B2', 'FileHelper::extensionMatch');
 
-		return true;
+		return $this->fileHelper->extensionMatch($filename, $file_types);
 	}
 
 	/**
 	 * Resorts uploaded files according to given file order
 	 *
 	 * @param Array|string $files
 	 * @return string
 	 * @access protected
 	 */
 	protected function _sortFiles($files)
 	{
 		if ( !is_array($files) ) {
 			$files = explode('|', $files);
 		}
 
 		$sorted_files = array_intersect($this->sorting, $files); // removes deleted files from sorting
 		$new_files = array_diff($files, $sorted_files); // files, that weren't sorted - add to the end
 
 		return implode('|', array_merge($sorted_files, $new_files));
 	}
 
 	/**
 	 * Returns maximal allowed file count per field
 	 *
 	 * @param Array $options
 	 * @return int
 	 * @access protected
 	 */
 	protected function _getMaxFiles($options)
 	{
 		if ( !isset($options['multiple']) ) {
 			return 1;
 		}
 
 		return $options['multiple'] == false ? 1 : $options['multiple'];
 	}
 
 	/**
 	 * Returns final filename after applying storage-engine specific naming
 	 *
 	 * @param string $file_name
 	 * @param Array $options
 	 * @param kDBItem $object
 	 * @param Array $files_to_delete
 	 * @return string
 	 * @access protected
 	 */
 	protected function _getRealFilename($file_name, $options, $object, $files_to_delete = Array ())
 	{
 		$real_name = $this->getStorageEngineFile($file_name, $options, $object->Prefix);
 		$real_name = $this->getStorageEngineFolder($real_name, $options) . $real_name;
 
 		return $this->fileHelper->ensureUniqueFilename($this->FullPath, $real_name, $files_to_delete);
 	}
 
 	/**
 	 * Renames file in sorting list
 	 *
 	 * @param string $old_name
 	 * @param string $new_name
 	 * @return void
 	 * @access protected
 	 */
 	protected function _renameFileInSorting($old_name, $new_name)
 	{
 		$index = array_search($old_name, $this->sorting);
 
 		if ( $index !== false ) {
 			$this->sorting[$index] = $new_name;
 		}
 	}
 
 	function getSingleFormat($format)
 	{
 		$single_mapping = Array (
 			'file_raw_urls' => 'raw_url',
 			'file_display_names' => 'display_name',
 			'file_urls' => 'full_url',
 			'file_paths' => 'full_path',
 			'file_sizes' => 'file_size',
 			'files_resized' => 'resize',
 			'img_sizes' => 'img_size',
 			'wms' => 'wm',
 		);
 
 		return $single_mapping[$format];
 	}
 
 	/**
 	 * Return formatted file url,path or size (or same for multiple files)
 	 *
 	 * @param string $value
 	 * @param string $field_name
 	 * @param kDBItem|kDBList $object
 	 * @param string $format
 	 * @return string
 	 */
 	function Format($value, $field_name, &$object, $format = NULL)
 	{
 		if ( is_null($value) ) {
 			return '';
 		}
 
 		$options = $object->GetFieldOptions($field_name);
 
 		if ( !isset($format) ) {
 			$format = isset($options['format']) ? $options['format'] : false;
 		}
 
 		if ( $format && preg_match('/(file_raw_urls|file_display_names|file_urls|file_paths|file_names|file_sizes|img_sizes|files_resized|wms)(.*)/', $format, $regs) ) {
 			if ( !$value || $format == 'file_names' ) {
 				// storage format matches display format OR no value
 				return $value;
 			}
 
 			$ret = Array ();
 			$files = explode('|', $value);
 			$format = $this->getSingleFormat($regs[1]) . $regs[2];
 
 			foreach ($files as $a_file) {
 				$ret[] = $this->GetFormatted($a_file, $field_name, $object, $format);
 			}
 
 			return implode('|', $ret);
 		}
 
 		$tc_value = $this->TypeCast($value, $options);
 
 		if ( ($tc_value === false) || ($tc_value != $value) ) {
 			// for leaving badly formatted date on the form
 			return $value;
 		}
 
 		return $this->GetFormatted($tc_value, $field_name, $object, $format);
 	}
 
 	/**
 	 * Return formatted file url,path or size
 	 *
 	 * @param string $value
 	 * @param string $field_name
 	 * @param kDBItem $object
 	 * @param string $format
 	 * @return string
 	 */
 	function GetFormatted($value, $field_name, &$object, $format = NULL)
 	{
 		if ( !$format ) {
 			return $value;
 		}
 
 		$options = $object->GetFieldOptions($field_name);
 		$upload_dir = isset($options['include_path']) && $options['include_path'] ? '' : $this->getUploadDir($options);
 		$file_path = strlen($value) ? FULL_PATH . str_replace('/', DIRECTORY_SEPARATOR, $upload_dir) . $value : '';
 
 		if ( preg_match('/resize:([\d]*)x([\d]*)/', $format, $regs) ) {
 			/** @var ImageHelper $image_helper */
 			$image_helper = $this->Application->recallObject('ImageHelper');
 
 			try {
 				return $image_helper->ResizeImage($file_path, $format);
 			}
 			catch ( RuntimeException $e ) {
 				// error, during image resize -> return empty string
 				return '';
 			}
 		}
 		elseif ( !strlen($file_path) || !file_exists($file_path) ) {
 			// file doesn't exist OR not uploaded
 			return '';
 		}
 
 		switch ($format) {
 			case 'display_name':
 				return kUtil::removeTempExtension($value);
 				break;
 
 			case 'raw_url':
 				return $this->fileHelper->pathToUrl($file_path);
 				break;
 
 			case 'full_url':
 				$direct_links = isset($options['direct_links']) ? $options['direct_links'] : true;
 
 				if ( $direct_links ) {
 					return $this->fileHelper->pathToUrl($file_path);
 				}
 				else {
 					$url_params = Array (
 						'pass' => 'm,'.$object->Prefix,
 						$object->Prefix . '_event' => 'OnViewFile',
 						'file' => $value, 'field' => $field_name
 					);
 
 					return $this->Application->HREF('', '', $url_params);
 				}
 				break;
 
 			case 'full_path':
 				return $file_path;
 				break;
 
 			case 'file_size':
 				return filesize($file_path);
 				break;
 
 			case 'img_size':
 				/** @var ImageHelper $image_helper */
 				$image_helper = $this->Application->recallObject('ImageHelper');
 
 				$image_info = $image_helper->getImageInfo($file_path);
 				return $image_info ? $image_info[3] : '';
 				break;
 		}
 
 		return sprintf($format, $value);
 	}
 
 	/**
 	 * Creates & returns folder, based on storage engine specified in field options
 	 *
 	 * @param string $file_name
 	 * @param array $options
 	 * @return string
 	 * @access protected
 	 * @throws Exception
 	 */
 	protected function getStorageEngineFolder($file_name, $options)
 	{
 		$storage_engine = (string)getArrayValue($options, 'storage_engine');
 
 		if ( !$storage_engine ) {
 			return '';
 		}
 
 		switch ($storage_engine) {
 			case StorageEngine::HASH:
 				$folder_path = kUtil::getHashPathForLevel($file_name);
 				break;
 
 			case StorageEngine::TIMESTAMP:
 				$folder_path = adodb_date('Y-m/d/');
 				break;
 
 			default:
 				throw new Exception('Unknown storage engine "<strong>' . $storage_engine . '</strong>".');
 				break;
 		}
 
 		return $folder_path;
 	}
 
 	/**
 	 * Applies prefix & suffix to uploaded filename, based on storage engine in field options
 	 *
 	 * @param string $name
 	 * @param array $options
 	 * @param string $unit_prefix
 	 * @return string
 	 * @access protected
 	 */
 	protected function getStorageEngineFile($name, $options, $unit_prefix)
 	{
 		$prefix = $this->getStorageEngineFilePart(getArrayValue($options, 'filename_prefix'), $unit_prefix);
 		$suffix = $this->getStorageEngineFilePart(getArrayValue($options, 'filename_suffix'), $unit_prefix);
 
 		$parts = pathinfo($name);
 
 		return ($prefix ? $prefix . '_' : '') . $parts['filename'] . ($suffix ? '_' . $suffix : '') . '.' . $parts['extension'];
 	}
 
 	/**
 	 * Creates prefix/suffix to join with uploaded file
 	 *
 	 * Added "u" before user_id to keep this value after FileHelper::ensureUniqueFilename method call
 	 *
 	 * @param string $option
 	 * @param string $unit_prefix
 	 * @return string
 	 * @access protected
 	 */
 	protected function getStorageEngineFilePart($option, $unit_prefix)
 	{
 		$replace_from = Array (
 			StorageEngine::PS_DATE_TIME, StorageEngine::PS_PREFIX, StorageEngine::PS_USER
 		);
 
 		$replace_to = Array (
 			adodb_date('Ymd-His'), $unit_prefix, 'u' . $this->Application->RecallVar('user_id')
 		);
 
 		return str_replace($replace_from, $replace_to, $option);
 	}
 
 	public function getUploadDir($options)
 	{
 		return isset($options['upload_dir']) ? $options['upload_dir'] : $this->DestinationPath;
 	}
 }
 
 
 class kPictureFormatter extends kUploadFormatter
 {
 	public function __construct()
 	{
 		$this->NakeLookupPath = IMAGES_PATH; // used ?
 		$this->DestinationPath = kUtil::constOn('ADMIN') ? IMAGES_PENDING_PATH : IMAGES_PATH;
 
 		parent::__construct();
 	}
 
 	/**
 	 * Return formatted file url,path or size
 	 *
 	 * @param string $value
 	 * @param string $field_name
 	 * @param kDBItem $object
 	 * @param string $format
 	 * @return string
 	 */
 	function GetFormatted($value, $field_name, &$object, $format = NULL)
 	{
 		if ( $format == 'img_size' ) {
 			$options = $object->GetFieldOptions($field_name);
 			$img_path = FULL_PATH . '/' . $this->getUploadDir($options) . $value;
 			$image_info = getimagesize($img_path);
 
 			return ' ' . $image_info[3];
 		}
 
 		return parent::GetFormatted($value, $field_name, $object, $format);
 	}
 }
Index: branches/5.2.x/core/units/helpers/file_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/file_helper.php	(revision 16686)
+++ branches/5.2.x/core/units/helpers/file_helper.php	(revision 16687)
@@ -1,484 +1,505 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class FileHelper extends kHelper {
 
 		/**
 		 * Puts existing item images (from sub-item) to virtual fields (in main item)
 		 *
 		 * @param kCatDBItem $object
 		 * @return void
 		 * @access public
 		 */
 		public function LoadItemFiles(&$object)
 		{
 			$max_file_count = $this->Application->ConfigValue($object->Prefix.'_MaxImageCount'); // file count equals to image count (temporary measure)
 
 			$sql = 'SELECT *
 					FROM '.TABLE_PREFIX.'CatalogFiles
 					WHERE ResourceId = '.$object->GetDBField('ResourceId').'
 					ORDER BY FileId ASC
 					LIMIT 0, '.(int)$max_file_count;
 			$item_files = $this->Conn->Query($sql);
 
 			$file_counter = 1;
 			foreach ($item_files as $item_file) {
 				$file_path = $item_file['FilePath'];
 				$object->SetDBField('File'.$file_counter, $file_path);
 				$object->SetOriginalField('File'.$file_counter, $file_path);
 				$object->SetFieldOption('File'.$file_counter, 'original_field', $item_file['FileName']);
 				$file_counter++;
 			}
 		}
 
 		/**
 		 * Saves newly uploaded images to external image table
 		 *
 		 * @param kCatDBItem $object
 		 * @return void
 		 * @access public
 		 */
 		public function SaveItemFiles(&$object)
 		{
 			$table_name = $this->Application->getUnitOption('#file', 'TableName');
 			$max_file_count = $this->Application->getUnitOption($object->Prefix, 'FileCount'); // $this->Application->ConfigValue($object->Prefix.'_MaxImageCount');
 
 			$this->CheckFolder(FULL_PATH . ITEM_FILES_PATH);
 
 			$i = 0;
 			while ($i < $max_file_count) {
 				$field = 'File'.($i + 1);
 				$field_options = $object->GetFieldOptions($field);
 
 				$file_path = $object->GetDBField($field);
 				if ($file_path) {
 					if (isset($field_options['original_field'])) {
 						$key_clause = 'FileName = '.$this->Conn->qstr($field_options['original_field']).' AND ResourceId = '.$object->GetDBField('ResourceId');
 
 						if ($object->GetDBField('Delete'.$field)) {
 							// if item was cloned, then new filename is in db (not in $image_src)
 							$sql = 'SELECT FilePath
 									FROM '.$table_name.'
 									WHERE '.$key_clause;
 							$file_path = $this->Conn->GetOne($sql);
 							if (@unlink(FULL_PATH.ITEM_FILES_PATH.$file_path)) {
 								$sql = 'DELETE FROM '.$table_name.'
 										WHERE '.$key_clause;
 								$this->Conn->Query($sql);
 							}
 						}
 						else {
 							// image record found -> update
 							$fields_hash = Array (
 								'FilePath' => $file_path,
 							);
 
 							$this->Conn->doUpdate($fields_hash, $table_name, $key_clause);
 						}
 					}
 					else {
 						// record not found -> create
 						$fields_hash = Array (
 							'ResourceId' => $object->GetDBField('ResourceId'),
 							'FileName' => $field,
 							'Status' => STATUS_ACTIVE,
 							'FilePath' => $file_path,
 						);
 
 						$this->Conn->doInsert($fields_hash, $table_name);
 						$field_options['original_field'] = $field;
 						$object->SetFieldOptions($field, $field_options);
 					}
 				}
 				$i++;
 			}
 		}
 
 		/**
 		 * Preserves cloned item images/files to be rewritten with original item images/files
 		 *
 		 * @param Array $field_values
 		 * @return void
 		 * @access public
 		 */
 		public function PreserveItemFiles(&$field_values)
 		{
 			foreach ($field_values as $field_name => $field_value) {
 				if ( !is_array($field_value) ) {
 					continue;
 				}
 
 				if ( isset($field_value['upload']) && ($field_value['error'] == UPLOAD_ERR_NO_FILE) ) {
 					// this is upload field, but nothing was uploaded this time
 					unset($field_values[$field_name]);
 				}
 			}
 		}
 
 		/**
 		 * Determines what image/file fields should be created (from post or just dummy fields for 1st upload)
 		 *
 		 * @param string $prefix
 		 * @param bool $is_image
 		 * @return void
 		 * @access public
 		 */
 		public function createItemFiles($prefix, $is_image = false)
 		{
 			$items_info = $this->Application->GetVar($prefix);
 			if ($items_info) {
 				list (, $fields_values) = each($items_info);
 				$this->createUploadFields($prefix, $fields_values, $is_image);
 			}
 			else {
 				$this->createUploadFields($prefix, Array(), $is_image);
 			}
 		}
 
 		/**
 		 * Dynamically creates virtual fields for item for each image/file field in submit
 		 *
 		 * @param string $prefix
 		 * @param Array $fields_values
 		 * @param bool $is_image
 		 * @return void
 		 * @access public
 		 */
 		public function createUploadFields($prefix, $fields_values, $is_image = false)
 		{
 			$field_options = Array (
 				'type'			=>	'string',
 				'max_len'		=>	240,
 				'default'		=>	'',
 			);
 
 			if ($is_image) {
 				$field_options['formatter'] = 'kPictureFormatter';
 				$field_options['include_path'] = 1;
 				$field_options['allowed_types'] = Array ('image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png', 'image/gif', 'image/bmp');
 				$field_prefix = 'Image';
 			}
 			else {
 				$field_options['formatter'] = 'kUploadFormatter';
 				$field_options['upload_dir'] = ITEM_FILES_PATH;
 				$field_options['allowed_types'] = Array ('application/pdf', 'application/msexcel', 'application/msword', 'application/mspowerpoint');
 				$field_prefix = 'File';
 			}
 
 			$fields = $this->Application->getUnitOption($prefix, 'Fields');
 			$virtual_fields = $this->Application->getUnitOption($prefix, 'VirtualFields');
 
 			$image_count = 0;
 			foreach ($fields_values as $field_name => $field_value) {
 				if (preg_match('/^('.$field_prefix.'[\d]+|Primary'.$field_prefix.')$/', $field_name)) {
 					$fields[$field_name] = $field_options;
 					$virtual_fields[$field_name] = $field_options;
 					$this->_createCustomFields($prefix, $field_name, $virtual_fields, $is_image);
 
 					$image_count++;
 				}
 			}
 
 			if (!$image_count) {
 				// no images found in POST -> create default image fields
 				$image_count = $this->Application->ConfigValue($prefix.'_MaxImageCount');
 
 				if ($is_image) {
 					$created_count = 1;
 					$image_names = Array ('Primary' . $field_prefix => '');
 
 					while ($created_count < $image_count) {
 						$image_names[$field_prefix . $created_count] = '';
 						$created_count++;
 					}
 				}
 				else {
 					$created_count = 0;
 					$image_names = Array ();
 
 					while ($created_count < $image_count) {
 						$image_names[$field_prefix . ($created_count + 1)] = '';
 						$created_count++;
 					}
 				}
 
 				if ($created_count) {
 					$this->createUploadFields($prefix, $image_names, $is_image);
 				}
 
 				return ;
 			}
 
 			$this->Application->setUnitOption($prefix, $field_prefix.'Count', $image_count);
 			$this->Application->setUnitOption($prefix, 'Fields', $fields);
 			$this->Application->setUnitOption($prefix, 'VirtualFields', $virtual_fields);
 		}
 
 		/**
 		 * Adds ability to create more virtual fields associated with main image/file
 		 *
 		 * @param string $prefix
 		 * @param string $field_name
 		 * @param Array $virtual_fields
 		 * @param bool $is_image
 		 * @return void
 		 * @access protected
 		 */
 		protected function _createCustomFields($prefix, $field_name, &$virtual_fields, $is_image = false)
 		{
 			$virtual_fields['Delete' . $field_name] = Array ('type' => 'int', 'default' => 0);
 
 			if ( $is_image ) {
 				$virtual_fields[$field_name . 'Alt'] = Array ('type' => 'string', 'default' => '');
 			}
 		}
 
 		/**
 		 * Downloads file to user
 		 *
 		 * @param string $filename
 		 * @return void
 		 * @access public
 		 */
 		public function DownloadFile($filename)
 		{
 			$this->Application->setContentType(kUtil::mimeContentType($filename), false);
 			header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
 			header('Content-Length: ' . filesize($filename));
 			readfile($filename);
 			flush();
 		}
 
 		/**
 		 * Creates folder with given $path
 		 *
 		 * @param string $path
 		 * @return bool
 		 * @access public
 		 */
 		public function CheckFolder($path)
 		{
 			$result = true;
 
 			if (!file_exists($path) || !is_dir($path)) {
 				$parent_path = preg_replace('#(/|\\\)[^/\\\]+(/|\\\)?$#', '', rtrim($path , '/\\'));
 				$result = $this->CheckFolder($parent_path);
 
 				if ($result) {
 					$result = mkdir($path);
 
 					if ($result) {
 						chmod($path, 0777);
 
 						// don't commit any files from created folder
 						if (file_exists(FULL_PATH . '/CVS')) {
 							$cvsignore = fopen($path . '/.cvsignore', 'w');
 							fwrite($cvsignore, '*.*');
 							fclose($cvsignore);
 							chmod($path . '/.cvsignore', 0777);
 						}
 					}
 					else {
 						trigger_error('Cannot create directory "<strong>' . $path . '</strong>"', E_USER_WARNING);
 						return false;
 					}
 				}
 			}
 
 			return $result;
 		}
 
 		/**
 		 * Copies all files and directories from $source to $destination directory. Create destination directory, when missing.
 		 *
 		 * @param string $source
 		 * @param string $destination
 		 * @return bool
 		 * @access public
 		 */
 		public function copyFolderRecursive($source, $destination)
 		{
 			if ( substr($source, -1) == DIRECTORY_SEPARATOR ) {
 				$source = substr($source, 0, -1);
 				$destination .= DIRECTORY_SEPARATOR . basename($source);
 			}
 
 			$iterator = new DirectoryIterator($source);
 			/** @var DirectoryIterator $file_info */
 
 			$result = $this->CheckFolder($destination);
 
 			foreach ($iterator as $file_info) {
 				if ( $file_info->isDot() ) {
 					continue;
 				}
 
 				$file = $file_info->getFilename();
 
 				if ( $file_info->isDir() ) {
 					$result = $this->copyFolderRecursive($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file);
 				}
 				else {
 					$result = copy($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file);
 				}
 
 				if (!$result) {
 					trigger_error('Cannot create file/directory "<strong>' . $destination . DIRECTORY_SEPARATOR . $file . '</strong>"', E_USER_WARNING);
 					break;
 				}
 			}
 
 			return $result;
 		}
 
 		/**
 		 * Copies all files from $source to $destination directory. Create destination directory, when missing.
 		 *
 		 * @param string $source
 		 * @param string $destination
 		 * @return bool
 		 * @access public
 		 */
 		public function copyFolder($source, $destination)
 		{
 			if ( substr($source, -1) == DIRECTORY_SEPARATOR ) {
 				$source = substr($source, 0, -1);
 				$destination .= DIRECTORY_SEPARATOR . basename($source);
 			}
 
 			$iterator = new DirectoryIterator($source);
 			/** @var DirectoryIterator $file_info */
 
 			$result = $this->CheckFolder($destination);
 
 			foreach ($iterator as $file_info) {
 				if ( $file_info->isDot() || !$file_info->isFile() ) {
 					continue;
 				}
 
 				$file = $file_info->getFilename();
 
 				$result = copy($file_info->getPathname(), $destination . DIRECTORY_SEPARATOR . $file);
 
 				if ( !$result ) {
 					trigger_error('Cannot create file "<strong>' . $destination . DIRECTORY_SEPARATOR . $file . '</strong>"', E_USER_WARNING);
 					break;
 				}
 			}
 
 			return $result;
 		}
 
 		/**
 		 * Transforms given path to file into it's url, where each each component is encoded (excluding domain and protocol)
 		 *
 		 * @param string $url
 		 * @return string
 		 * @access public
 		 */
 		public function pathToUrl($url)
 		{
 			$url = str_replace(DIRECTORY_SEPARATOR, '/', preg_replace('/^' . preg_quote(FULL_PATH, '/') . '(.*)/', '\\1', $url, 1));
 
 			// TODO: why?
 			$url = implode('/', array_map('rawurlencode', explode('/', $url)));
 
 			return rtrim($this->Application->BaseURL(), '/') . $url;
 		}
 
 		/**
 		 * Transforms given url to path to it
 		 *
 		 * @param string $url
 		 * @return string
 		 * @access public
 		 */
 		public function urlToPath($url)
 		{
 			$base_url = rtrim($this->Application->BaseURL(), '/');
 
 			// escape replacement patterns, like "\<number>"
 			$full_path = preg_replace('/(\\\[\d]+)/', '\\\\\1', FULL_PATH);
 			$path = preg_replace('/^' . preg_quote($base_url, '/') . '(.*)/', $full_path . '\\1', $url, 1);
 
 			return str_replace('/', DIRECTORY_SEPARATOR, kUtil::unescape($path, kUtil::ESCAPE_URL));
 		}
 
 		/**
 		 * Makes given paths DocumentRoot agnostic.
 		 *
 		 * @param array $paths List of file paths.
 		 *
 		 * @return array
 		 */
 		public function makeRelative(array $paths)
 		{
 			foreach ( $paths as $index => $path ) {
 				$replaced_count = 0;
 				$relative_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $path, 1, $replaced_count);
 
 				if ( $replaced_count === 1 ) {
 					$paths[$index] = $relative_path;
 				}
 			}
 
 			return $paths;
 		}
 
 		/**
 		 * Ensures, that new file will not overwrite any of previously created files with same name
 		 *
 		 * @param string $path
 		 * @param string $name
 		 * @param Array $forbidden_names
 		 * @return string
 		 */
 		public function ensureUniqueFilename($path, $name, $forbidden_names = Array ())
 		{
 			$parts = pathinfo($name);
 			$ext = '.' . $parts['extension'];
 			$filename = $parts['filename'];
 			$path = rtrim($path, '/');
 
 			$original_checked = false;
 			$new_name = $filename . $ext;
 
 			if ( $parts['dirname'] != '.' ) {
 				$path .= '/' . ltrim($parts['dirname'], '/');
 			}
 
 			// make sure target folder always exists, especially for cases,
 			// when storage engine folder is supplied as a part of $name
 			$this->CheckFolder($path);
 
 			while (file_exists($path . '/' . $new_name) || in_array($path . '/' . $new_name, $forbidden_names)) {
 				if ( preg_match('/(.*)_([0-9]*)(' . preg_quote($ext, '/') . ')/', $new_name, $regs) ) {
 					$new_name = $regs[1] . '_' . ((int)$regs[2] + 1) . $regs[3];
 				}
 				elseif ( $original_checked ) {
 					$new_name = $filename . '_1' . $ext;
 				}
 
 				$original_checked = true;
 			}
 
 			if ( $parts['dirname'] != '.' ) {
 				$new_name = $parts['dirname'] . '/' . $new_name;
 			}
 
 			return $new_name;
 		}
+
+		/**
+		 * Checks, that given file name has on of provided file extensions
+		 *
+		 * @param string $filename   Filename.
+		 * @param string $file_types File types.
+		 *
+		 * @return boolean
+		 */
+		public function extensionMatch($filename, $file_types)
+		{
+			if ( preg_match_all('/\*\.(.*?)(;|$)/', $file_types, $regs) ) {
+				$file_extension = mb_strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+				$file_extensions = array_map('mb_strtolower', $regs[1]);
+
+				return in_array($file_extension, $file_extensions);
+			}
+
+			return true;
+		}
+
 	}
Index: branches/5.2.x/core/units/helpers/upload_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/upload_helper.php	(revision 16686)
+++ branches/5.2.x/core/units/helpers/upload_helper.php	(revision 16687)
@@ -1,404 +1,418 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2012 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 class kUploadHelper extends kHelper
 {
 
 	/**
 	 * File helper reference
 	 *
 	 * @var FileHelper
 	 */
 	protected $fileHelper = null;
 
 	/**
 	 * Creates kUploadHelper instance.
 	 */
 	public function __construct()
 	{
 		parent::__construct();
 
 		$this->fileHelper = $this->Application->recallObject('FileHelper');
 
 		// 5 minutes execution time
 		@set_time_limit(5 * 60);
 	}
 
 	/**
 	 * Handles the upload.
 	 *
 	 * @param kEvent $event Event.
 	 *
 	 * @return string
 	 * @throws kUploaderException When upload could not be handled properly.
 	 */
 	public function handle(kEvent $event)
 	{
 		$this->disableBrowserCache();
 
 //		Uncomment this one to fake upload time
 //		sleep(5);
 
 		if ( !$this->Application->HttpQuery->Post ) {
 			// Variables {field, id, flashsid} are always submitted through POST!
 			// When file size is larger, then "upload_max_filesize" (in php.ini),
 			// then these variables also are not submitted.
 			throw new kUploaderException('File size exceeds allowed limit.', 413);
 		}
 
 		if ( !$this->checkPermissions($event) ) {
 			// 403 Forbidden
 			throw new kUploaderException('You don\'t have permissions to upload.', 403);
 		}
 
 		$value = $this->Application->GetVar('file');
 
 		if ( !$value || ($value['error'] != UPLOAD_ERR_OK) ) {
 			// 413 Request Entity Too Large (file uploads disabled OR uploaded file was
 			// too large for web server to accept, see "upload_max_filesize" in php.ini)
 			throw new kUploaderException('File size exceeds allowed limit.', 413);
 		}
 
 		$value = $this->Application->unescapeRequestVariable($value);
 
 		$tmp_path = WRITEABLE . '/tmp/';
 		$filename = $this->getUploadedFilename() . '.tmp';
 		$id = $this->Application->GetVar('id');
 
 		if ( $id ) {
 			$filename = $id . '_' . $filename;
 		}
 
 		if ( !is_writable($tmp_path) ) {
 			// 500 Internal Server Error
 			// check both temp and live upload directory
 			throw new kUploaderException('Write permissions not set on the server, please contact server administrator.', 500);
 		}
 
 		$filename = $this->fileHelper->ensureUniqueFilename($tmp_path, $filename);
-		$storage_format = $this->getStorageFormat($this->Application->GetVar('field'), $event);
+		$field_options = $this->getFieldOptions($this->Application->GetVar('field'), $event);
+		$storage_format = isset($field_options['storage_format']) ? $field_options['storage_format'] : false;
 
 		$file_path = $tmp_path . $filename;
 		$actual_file_path = $this->moveUploadedFile($file_path);
 
 		if ( $storage_format && $file_path == $actual_file_path ) {
 			$this->resizeUploadedFile($file_path, $storage_format);
 		}
 
+		if ( getArrayValue($field_options, 'file_types')
+			&& !$this->fileHelper->extensionMatch(kUtil::removeTempExtension($filename), $field_options['file_types'])
+		) {
+			throw new kUploaderException('File is not an allowed file type.', 415);
+		}
+
+		if ( filesize($actual_file_path) > $field_options['max_size'] ) {
+			throw new kUploaderException('File size exceeds allowed limit.', 413);
+		}
+
 		$this->deleteTempFiles($tmp_path);
 
 		$thumbs_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $tmp_path, 1);
 		$thumbs_path = FULL_PATH . THUMBS_PATH . $thumbs_path;
 
 		if ( file_exists($thumbs_path) ) {
 			$this->deleteTempFiles($thumbs_path);
 		}
 
 		return preg_replace('/^' . preg_quote($id, '/') . '_/', '', basename($file_path));
 	}
 
 	/**
 	 * Resizes uploaded file.
 	 *
 	 * @param string $file_path File path.
 	 * @param string $format    Format.
 	 *
 	 * @return boolean
 	 */
 	public function resizeUploadedFile(&$file_path, $format)
 	{
 		/** @var ImageHelper $image_helper */
 		$image_helper = $this->Application->recallObject('ImageHelper');
 
 		// Add extension, so that "ImageHelper::ResizeImage" can work.
 		$resize_file_path = tempnam(WRITEABLE . '/tmp', 'uploaded_') . '.jpg';
 
 		if ( rename($file_path, $resize_file_path) === false ) {
 			return false;
 		}
 
 		$resized_file_path = $this->fileHelper->urlToPath(
 			$image_helper->ResizeImage($resize_file_path, $format)
 		);
 
 		$file_path = $this->replaceFileExtension(
 			$file_path,
 			pathinfo($resized_file_path, PATHINFO_EXTENSION)
 		);
 
 		return rename($resized_file_path, $file_path);
 	}
 
 	/**
 	 * Replace extension of uploaded file.
 	 *
 	 * @param string $file_path          File path.
 	 * @param string $new_file_extension New file extension.
 	 *
 	 * @return string
 	 */
 	protected function replaceFileExtension($file_path, $new_file_extension)
 	{
 		$file_path_without_temp_file_extension = kUtil::removeTempExtension($file_path);
 		$current_file_extension = pathinfo($file_path_without_temp_file_extension, PATHINFO_EXTENSION);
 
 		// Format of resized file wasn't changed.
 		if ( $current_file_extension === $new_file_extension ) {
 			return $file_path;
 		}
 
 		$ret = preg_replace(
 			'/\.' . preg_quote($current_file_extension, '/') . '$/',
 			'.' . $new_file_extension,
 			$file_path_without_temp_file_extension
 		);
 
 		// Add ".tmp" later, since it was removed.
 		if ( $file_path_without_temp_file_extension !== $file_path ) {
 			$ret .= '.tmp';
 		}
 
 		// After file extension change resulting filename might not be unique in that folder anymore.
 		$path = pathinfo($ret, PATHINFO_DIRNAME);
 
 		return $path . '/' . $this->fileHelper->ensureUniqueFilename($path, basename($ret));
 	}
 
 	/**
 	 * Sends headers to ensure, that response is never cached.
 	 *
 	 * @return void
 	 */
 	protected function disableBrowserCache()
 	{
 		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
 		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
 		header('Cache-Control: no-store, no-cache, must-revalidate');
 		header('Cache-Control: post-check=0, pre-check=0', false);
 		header('Pragma: no-cache');
 	}
 
 	/**
 	 * Checks, that flash uploader is allowed to perform upload
 	 *
 	 * @param kEvent $event
 	 * @return bool
 	 */
 	protected function checkPermissions(kEvent $event)
 	{
 		// Flash uploader does NOT send correct cookies, so we need to make our own check
 		$cookie_name = 'adm_' . $this->Application->ConfigValue('SessionCookieName');
 		$this->Application->HttpQuery->Cookie['cookies_on'] = 1;
 		$this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid');
 
 		// this prevents session from auto-expiring when KeepSessionOnBrowserClose & FireFox is used
 		$this->Application->HttpQuery->Cookie[$cookie_name . '_live'] = $this->Application->GetVar('flashsid');
 
 		/** @var Session $admin_session */
 		$admin_session = $this->Application->recallObject('Session.admin');
 
 		if ( $this->Application->permissionCheckingDisabled($admin_session->RecallVar('user_id')) ) {
 			return true;
 		}
 
 		// copy some data from given session to current session
 		$backup_user_id = $this->Application->RecallVar('user_id');
 		$this->Application->StoreVar('user_id', $admin_session->RecallVar('user_id'));
 
 		$backup_user_groups = $this->Application->RecallVar('UserGroups');
 		$this->Application->StoreVar('UserGroups', $admin_session->RecallVar('UserGroups'));
 
 		// check permissions using event, that have "add|edit" rule
 		$check_event = new kEvent($event->getPrefixSpecial() . ':OnProcessSelected');
 		$check_event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
 
 		/** @var kEventHandler $event_handler */
 		$event_handler = $this->Application->recallObject($event->Prefix . '_EventHandler');
 		$allowed_to_upload = $event_handler->CheckPermission($check_event);
 
 		// restore changed data, so nothing gets saved to database
 		$this->Application->StoreVar('user_id', $backup_user_id);
 		$this->Application->StoreVar('UserGroups', $backup_user_groups);
 
 		return $allowed_to_upload;
 	}
 
 	/**
 	 * Returns uploaded filename.
 	 *
 	 * @return string
 	 */
 	protected function getUploadedFilename()
 	{
 		if ( isset($_REQUEST['name']) ) {
 			$file_name = $_REQUEST['name'];
 		}
 		elseif ( !empty($_FILES) ) {
 			$file_name = $_FILES['file']['name'];
 		}
 		else {
 			$file_name = uniqid('file_');
 		}
 
 		return $file_name;
 	}
 
 	/**
-	 * Gets storage format for a given field.
+	 * Returns field options.
 	 *
-	 * @param string $field_name
-	 * @param kEvent $event
-	 * @return bool
+	 * @param string $field Field.
+	 * @param kEvent $event Event.
+	 *
+	 * @return array
 	 */
-	protected function getStorageFormat($field_name, kEvent $event)
+	protected function getFieldOptions($field, kEvent $event)
 	{
+		/** @var array $fields */
 		$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
+
+		/** @var array $virtual_fields */
 		$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
-		$field_options = array_key_exists($field_name, $fields) ? $fields[$field_name] : $virtual_fields[$field_name];
 
-		return isset($field_options['storage_format']) ? $field_options['storage_format'] : false;
+		return array_key_exists($field, $fields) ? $fields[$field] : $virtual_fields[$field];
 	}
 
 	/**
 	 * Moves uploaded file to given location.
 	 *
 	 * @param string $file_path File path.
 	 *
 	 * @return string
 	 * @throws kUploaderException When upload could not be handled properly.
 	 */
 	protected function moveUploadedFile($file_path)
 	{
 		// Chunking might be enabled.
 		$chunk = (int)$this->Application->GetVar('chunk', 0);
 		$chunks = (int)$this->Application->GetVar('chunks', 0);
 		$actual_file_path = $file_path . '.part';
 
 		// Open temp file.
 		if ( !$out = @fopen($actual_file_path, $chunks ? 'ab' : 'wb') ) {
 			throw new kUploaderException('Failed to open output stream.', 102);
 		}
 
 		if ( !empty($_FILES) ) {
 			if ( $_FILES['file']['error'] || !is_uploaded_file($_FILES['file']['tmp_name']) ) {
 				throw new kUploaderException('Failed to move uploaded file.', 103);
 			}
 
 			// Read binary input stream and append it to temp file.
 			if ( !$in = @fopen($_FILES['file']['tmp_name'], 'rb') ) {
 				throw new kUploaderException('Failed to open input stream.', 101);
 			}
 		}
 		else {
 			if ( !$in = @fopen('php://input', 'rb') ) {
 				throw new kUploaderException('Failed to open input stream.', 101);
 			}
 		}
 
 		while ( $buff = fread($in, 4096) ) {
 			fwrite($out, $buff);
 		}
 
 		@fclose($out);
 		@fclose($in);
 
 		// Check if file has been uploaded.
 		if ( !$chunks || $chunk == $chunks - 1 ) {
 			// Strip the temp .part suffix off.
 			rename($actual_file_path, $file_path);
 			$actual_file_path = $file_path;
 		}
 
 		return $actual_file_path;
 	}
 
 	/**
 	 * Delete temporary files, that won't be used for sure
 	 *
 	 * @param string $path
 	 * @return void
 	 */
 	protected function deleteTempFiles($path)
 	{
 		$files = glob($path . '*.*');
 		$max_file_date = strtotime('-1 day');
 
 		foreach ( $files as $file ) {
 			if ( filemtime($file) < $max_file_date ) {
 				unlink($file);
 			}
 		}
 	}
 
 	/**
 	 * Prepares object for operations with file on given field.
 	 *
 	 * @param kEvent $event Event.
 	 * @param string $field Field.
 	 *
 	 * @return kDBItem
 	 */
 	public function prepareUploadedFile(kEvent $event, $field)
 	{
 		/** @var kDBItem $object */
 		$object = $event->getObject(Array ('skip_autoload' => true));
 
 		$filename = $this->getSafeFilename();
 
 		if ( !$filename ) {
 			$object->SetDBField($field, '');
 
 			return $object;
 		}
 
 		// set current uploaded file
 		if ( $this->Application->GetVar('tmp') ) {
 			$options = $object->GetFieldOptions($field);
 			$options['upload_dir'] = WRITEBALE_BASE . '/tmp/';
 			unset($options['include_path']);
 			$object->SetFieldOptions($field, $options);
 
 			$filename = $this->Application->GetVar('id') . '_' . $filename;
 		}
 
 		$object->SetDBField($field, $filename);
 
 		return $object;
 	}
 
 	/**
 	 * Returns safe version of filename specified in url
 	 *
 	 * @return bool|string
 	 * @access protected
 	 */
 	protected function getSafeFilename()
 	{
 		$filename = $this->Application->GetVar('file');
 		$filename = $this->Application->unescapeRequestVariable($filename);
 
 		if ( (strpos($filename, '../') !== false) || (trim($filename) !== $filename) ) {
 			// when relative paths or special chars are found template names from url, then it's hacking attempt
 			return false;
 		}
 
 		return $filename;
 	}
 }
 
 
 class kUploaderException extends Exception
 {
 
 }