diff options
39 files changed, 2988 insertions, 1272 deletions
diff --git a/phpBB/composer.json b/phpBB/composer.json index 70319fe3fb..bcf359a53b 100644 --- a/phpBB/composer.json +++ b/phpBB/composer.json @@ -25,6 +25,7 @@  		"phpbb/phpbb-core": "self.version"  	},  	"require": { +		"bantu/ini-get-wrapper": "1.0.*",  		"lusitanian/oauth": "0.2.*",  		"marc1706/fast-image-size": "1.1.*",  		"patchwork/utf8": "1.1.*", diff --git a/phpBB/composer.lock b/phpBB/composer.lock index 7d45abc6e3..accd4a55f6 100644 --- a/phpBB/composer.lock +++ b/phpBB/composer.lock @@ -4,9 +4,39 @@          "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",          "This file is @generated automatically"      ], -    "hash": "da690ad4ab4884661792b4b9d648934a", +    "hash": "983770c26a0dcd7399ebd488846ade9a",      "packages": [          { +            "name": "bantu/ini-get-wrapper", +            "version": "v1.0.1", +            "source": { +                "type": "git", +                "url": "https://github.com/bantuXorg/php-ini-get-wrapper.git", +                "reference": "4770c7feab370c62e23db4f31c112b7c6d90aee2" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/bantuXorg/php-ini-get-wrapper/zipball/4770c7feab370c62e23db4f31c112b7c6d90aee2", +                "reference": "4770c7feab370c62e23db4f31c112b7c6d90aee2", +                "shasum": "" +            }, +            "require-dev": { +                "phpunit/phpunit": "3.7.*" +            }, +            "type": "library", +            "autoload": { +                "psr-4": { +                    "bantu\\IniGetWrapper\\": "src/" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "description": "Convenience wrapper around ini_get()", +            "time": "2014-09-15 13:12:35" +        }, +        {              "name": "lusitanian/oauth",              "version": "v0.2.1",              "source": { diff --git a/phpBB/config/default/container/services_avatar.yml b/phpBB/config/default/container/services_avatar.yml index 1b0c109298..76057cf5a7 100644 --- a/phpBB/config/default/container/services_avatar.yml +++ b/phpBB/config/default/container/services_avatar.yml @@ -63,8 +63,8 @@ services:              - %core.php_ext%              - @filesystem              - @path_helper -            - @mimetype.guesser              - @dispatcher +            - @files.factory              - @cache.driver          calls:              - [set_name, [avatar.driver.upload]] diff --git a/phpBB/config/default/container/services_files.yml b/phpBB/config/default/container/services_files.yml index 828f9076dd..cfdade37df 100644 --- a/phpBB/config/default/container/services_files.yml +++ b/phpBB/config/default/container/services_files.yml @@ -1,3 +1,60 @@  services:      filesystem:          class: phpbb\filesystem\filesystem + +    files.factory: +        class: phpbb\files\factory +        arguments: +            - @service_container + +    files.filespec: +        class: phpbb\files\filespec +        scope: prototype +        arguments: +            - @filesystem +            - @language +            - @php_ini +            - @upload_imagesize +            - %core.root_path% +            - @mimetype.guesser +            - @plupload + +    files.upload: +        class: phpbb\files\upload +        scope: prototype +        arguments: +            - @filesystem +            - @files.factory +            - @language +            - @php_ini +            - @request +            - %core.root_path% + +    files.types.form: +        class: phpbb\files\types\form +        scope: prototype +        arguments: +            - @files.factory +            - @language +            - @php_ini +            - @plupload +            - @request + +    files.types.local: +        class: phpbb\files\types\form +        scope: prototype +        arguments: +            - @files.factory +            - @language +            - @php_ini +            - @request + +    files.types.remote: +        class: phpbb\files\types\remote +        scope: prototype +        arguments: +            - @files.factory +            - @language +            - @php_ini +            - @request +            - %core.root_path% diff --git a/phpBB/config/default/container/services_php.yml b/phpBB/config/default/container/services_php.yml index 8aabc7341f..29349960f3 100644 --- a/phpBB/config/default/container/services_php.yml +++ b/phpBB/config/default/container/services_php.yml @@ -1,3 +1,3 @@  services:      php_ini: -        class: phpbb\php\ini +        class: bantu\IniGetWrapper\IniGetWrapper diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php index 4206c87eb9..8b17dba534 100644 --- a/phpBB/includes/functions_posting.php +++ b/phpBB/includes/functions_posting.php @@ -405,14 +405,13 @@ function posting_gen_topic_types($forum_id, $cur_topic_type = POST_NORMAL)  function upload_attachment($form_name, $forum_id, $local = false, $local_storage = '', $is_message = false, $local_filedata = false, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null)  {  	global $auth, $user, $config, $db, $cache; -	global $phpbb_root_path, $phpEx, $phpbb_dispatcher, $phpbb_filesystem; +	global $phpbb_root_path, $phpEx, $phpbb_dispatcher, $phpbb_container;  	$filedata = array(  		'error'	=> array()  	); -	include_once($phpbb_root_path . 'includes/functions_upload.' . $phpEx); -	$upload = new fileupload($phpbb_filesystem); +	$upload = $phpbb_container->get('files.upload');  	if ($config['check_attachment_content'] && isset($config['mime_triggers']))  	{ @@ -434,9 +433,10 @@ function upload_attachment($form_name, $forum_id, $local = false, $local_storage  	$extensions = $cache->obtain_attach_extensions((($is_message) ? false : (int) $forum_id));  	$upload->set_allowed_extensions(array_keys($extensions['_allowed_'])); -	$file = ($local) ? $upload->local_upload($local_storage, $local_filedata, $mimetype_guesser) : $upload->form_upload($form_name, $mimetype_guesser, $plupload); +	/** @var \phpbb\files\filespec $file */ +	$file = ($local) ? $upload->handle_upload('files.types.local', $local_storage, $local_filedata) : $upload->handle_upload('files.types.form', $form_name); -	if ($file->init_error) +	if ($file->init_error())  	{  		$filedata['post_attach'] = false;  		return $filedata; diff --git a/phpBB/includes/functions_upload.php b/phpBB/includes/functions_upload.php deleted file mode 100644 index 3ab87ee794..0000000000 --- a/phpBB/includes/functions_upload.php +++ /dev/null @@ -1,1129 +0,0 @@ -<?php -/** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ - -/** -* @ignore -*/ -if (!defined('IN_PHPBB')) -{ -	exit; -} - -/** -* Responsible for holding all file relevant information, as well as doing file-specific operations. -* The {@link fileupload fileupload class} can be used to upload several files, each of them being this object to operate further on. -*/ -class filespec -{ -	var $filename = ''; -	var $realname = ''; -	var $uploadname = ''; -	var $mimetype = ''; -	var $extension = ''; -	var $filesize = 0; -	var $width = 0; -	var $height = 0; -	var $image_info = array(); - -	var $destination_file = ''; -	var $destination_path = ''; - -	var $file_moved = false; -	var $init_error = false; -	var $local = false; - -	var $error = array(); - -	var $upload = ''; - -	/** -	 * @var \phpbb\filesystem\filesystem_interface -	 */ -	protected $filesystem; - -	/** -	 * The plupload object -	 * @var \phpbb\plupload\plupload -	 */ -	protected $plupload; - -	/** -	 * phpBB Mimetype guesser -	 * @var \phpbb\mimetype\guesser -	 */ -	protected $mimetype_guesser; - -	/** -	* File Class -	* @access private -	*/ -	function filespec($upload_ary, $upload_namespace, \phpbb\filesystem\filesystem_interface $phpbb_filesystem, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null) -	{ -		if (!isset($upload_ary)) -		{ -			$this->init_error = true; -			return; -		} - -		$this->filename = $upload_ary['tmp_name']; -		$this->filesize = $upload_ary['size']; -		$name = (STRIP) ? stripslashes($upload_ary['name']) : $upload_ary['name']; -		$name = trim(utf8_basename($name)); -		$this->realname = $this->uploadname = $name; -		$this->mimetype = $upload_ary['type']; - -		// Opera adds the name to the mime type -		$this->mimetype	= (strpos($this->mimetype, '; name') !== false) ? str_replace(strstr($this->mimetype, '; name'), '', $this->mimetype) : $this->mimetype; - -		if (!$this->mimetype) -		{ -			$this->mimetype = 'application/octet-stream'; -		} - -		$this->extension = strtolower(self::get_extension($this->realname)); - -		// Try to get real filesize from temporary folder (not always working) ;) -		$this->filesize = (@filesize($this->filename)) ? @filesize($this->filename) : $this->filesize; - -		$this->width = $this->height = 0; -		$this->file_moved = false; - -		$this->local = (isset($upload_ary['local_mode'])) ? true : false; -		$this->upload = $upload_namespace; -		$this->plupload = $plupload; -		$this->mimetype_guesser = $mimetype_guesser; -		$this->filesystem = $phpbb_filesystem; -	} - -	/** -	* Cleans destination filename -	* -	* @param real|unique|unique_ext $mode real creates a realname, filtering some characters, lowering every character. Unique creates an unique filename -	* @param string $prefix Prefix applied to filename -	* @param string $user_id The user_id is only needed for when cleaning a user's avatar -	* @access public -	*/ -	function clean_filename($mode = 'unique', $prefix = '', $user_id = '') -	{ -		if ($this->init_error) -		{ -			return; -		} - -		switch ($mode) -		{ -			case 'real': -				// Remove every extension from filename (to not let the mime bug being exposed) -				if (strpos($this->realname, '.') !== false) -				{ -					$this->realname = substr($this->realname, 0, strpos($this->realname, '.')); -				} - -				// Replace any chars which may cause us problems with _ -				$bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|'); - -				$this->realname = rawurlencode(str_replace($bad_chars, '_', strtolower($this->realname))); -				$this->realname = preg_replace("/%(\w{2})/", '_', $this->realname); - -				$this->realname = $prefix . $this->realname . '.' . $this->extension; -			break; - -			case 'unique': -				$this->realname = $prefix . md5(unique_id()); -			break; - -			case 'avatar': -				$this->extension = strtolower($this->extension); -				$this->realname = $prefix . $user_id . '.' . $this->extension; - -			break; - -			case 'unique_ext': -			default: -				$this->realname = $prefix . md5(unique_id()) . '.' . $this->extension; -			break; -		} -	} - -	/** -	* Get property from file object -	*/ -	function get($property) -	{ -		if ($this->init_error || !isset($this->$property)) -		{ -			return false; -		} - -		return $this->$property; -	} - -	/** -	* Check if file is an image (mimetype) -	* -	* @return true if it is an image, false if not -	*/ -	function is_image() -	{ -		return (strpos($this->mimetype, 'image/') === 0); -	} - -	/** -	* Check if the file got correctly uploaded -	* -	* @return true if it is a valid upload, false if not -	*/ -	function is_uploaded() -	{ -		$is_plupload = $this->plupload && $this->plupload->is_active(); - -		if (!$this->local && !$is_plupload && !is_uploaded_file($this->filename)) -		{ -			return false; -		} - -		if (($this->local || $is_plupload) && !file_exists($this->filename)) -		{ -			return false; -		} - -		return true; -	} - -	/** -	* Remove file -	*/ -	function remove() -	{ -		if ($this->file_moved) -		{ -			@unlink($this->destination_file); -		} -	} - -	/** -	* Get file extension -	* -	* @param string Filename that needs to be checked -	* @return string Extension of the supplied filename -	*/ -	static public function get_extension($filename) -	{ -		$filename = utf8_basename($filename); - -		if (strpos($filename, '.') === false) -		{ -			return ''; -		} - -		$filename = explode('.', $filename); -		return array_pop($filename); -	} - -	/** -	* Get mimetype -	* -	* @param string $filename Filename that needs to be checked -	* @return string Mimetype of supplied filename -	*/ -	function get_mimetype($filename) -	{ -		if ($this->mimetype_guesser !== null) -		{ -			$mimetype = $this->mimetype_guesser->guess($filename, $this->uploadname); - -			if ($mimetype !== 'application/octet-stream') -			{ -				$this->mimetype = $mimetype; -			} -		} - -		return $this->mimetype; -	} - -	/** -	* Get filesize -	*/ -	function get_filesize($filename) -	{ -		return @filesize($filename); -	} - - -	/** -	* Check the first 256 bytes for forbidden content -	*/ -	function check_content($disallowed_content) -	{ -		if (empty($disallowed_content)) -		{ -			return true; -		} - -		$fp = @fopen($this->filename, 'rb'); - -		if ($fp !== false) -		{ -			$ie_mime_relevant = fread($fp, 256); -			fclose($fp); -			foreach ($disallowed_content as $forbidden) -			{ -				if (stripos($ie_mime_relevant, '<' . $forbidden) !== false) -				{ -					return false; -				} -			} -		} -		return true; -	} - -	/** -	* Move file to destination folder -	* The phpbb_root_path variable will be applied to the destination path -	* -	* @param string $destination Destination path, for example $config['avatar_path'] -	* @param bool $overwrite If set to true, an already existing file will be overwritten -	* @param bool $skip_image_check If set to true, the check for the file to be a valid image is skipped -	* @param string $chmod Permission mask for chmodding the file after a successful move. The mode entered here reflects the mode defined by {@link phpbb_chmod()} -	* -	* @access public -	*/ -	function move_file($destination, $overwrite = false, $skip_image_check = false, $chmod = false) -	{ -		global $user, $phpbb_root_path; - -		if (sizeof($this->error)) -		{ -			return false; -		} - -		$chmod = ($chmod === false) ? CHMOD_READ | CHMOD_WRITE : $chmod; - -		// We need to trust the admin in specifying valid upload directories and an attacker not being able to overwrite it... -		$this->destination_path = $phpbb_root_path . $destination; - -		// Check if the destination path exist... -		if (!file_exists($this->destination_path)) -		{ -			@unlink($this->filename); -			return false; -		} - -		$upload_mode = (@ini_get('open_basedir') || @ini_get('safe_mode') || strtolower(@ini_get('safe_mode')) == 'on') ? 'move' : 'copy'; -		$upload_mode = ($this->local) ? 'local' : $upload_mode; -		$this->destination_file = $this->destination_path . '/' . utf8_basename($this->realname); - -		// Check if the file already exist, else there is something wrong... -		if (file_exists($this->destination_file) && !$overwrite) -		{ -			@unlink($this->filename); -			$this->error[] = $user->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file); -			$this->file_moved = false; -			return false; -		} -		else -		{ -			if (file_exists($this->destination_file)) -			{ -				@unlink($this->destination_file); -			} - -			switch ($upload_mode) -			{ -				case 'copy': - -					if (!@copy($this->filename, $this->destination_file)) -					{ -						if (!@move_uploaded_file($this->filename, $this->destination_file)) -						{ -							$this->error[] = sprintf($user->lang[$this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR'], $this->destination_file); -						} -					} - -				break; - -				case 'move': - -					if (!@move_uploaded_file($this->filename, $this->destination_file)) -					{ -						if (!@copy($this->filename, $this->destination_file)) -						{ -							$this->error[] = sprintf($user->lang[$this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR'], $this->destination_file); -						} -					} - -				break; - -				case 'local': - -					if (!@copy($this->filename, $this->destination_file)) -					{ -						$this->error[] = sprintf($user->lang[$this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR'], $this->destination_file); -					} - -				break; -			} - -			// Remove temporary filename -			@unlink($this->filename); - -			if (sizeof($this->error)) -			{ -				return false; -			} - -			try -			{ -				$this->filesystem->phpbb_chmod($this->destination_file, $chmod); -			} -			catch (\phpbb\filesystem\exception\filesystem_exception $e) -			{ -				// Do nothing -			} -		} - -		// Try to get real filesize from destination folder -		$this->filesize = (@filesize($this->destination_file)) ? @filesize($this->destination_file) : $this->filesize; - -		// Get mimetype of supplied file -		$this->mimetype = $this->get_mimetype($this->destination_file); - -		if ($this->is_image() && !$skip_image_check) -		{ -			$this->width = $this->height = 0; - -			// Get imagesize class -			$imagesize = new \FastImageSize\FastImageSize(); - -			$this->image_info = $imagesize->getImageSize($this->destination_file, $this->mimetype); - -			if ($this->image_info !== false) -			{ -				$this->width = $this->image_info['width']; -				$this->height = $this->image_info['height']; - -				// Check image type -				$types = fileupload::image_types(); - -				if (!isset($types[$this->image_info['type']]) || !in_array($this->extension, $types[$this->image_info['type']])) -				{ -					if (!isset($types[$this->image_info['type']])) -					{ -						$this->error[] = $user->lang('IMAGE_FILETYPE_INVALID', $this->image_info['type'], $this->mimetype); -					} -					else -					{ -						$this->error[] = $user->lang('IMAGE_FILETYPE_MISMATCH', $types[$this->image_info['type']][0], $this->extension); -					} -				} - -				// Make sure the dimensions match a valid image -				if (empty($this->width) || empty($this->height)) -				{ -					$this->error[] = $user->lang['ATTACHED_IMAGE_NOT_IMAGE']; -				} -			} -			else -			{ -				$this->error[] = $user->lang['UNABLE_GET_IMAGE_SIZE']; -			} -		} - -		$this->file_moved = true; -		$this->additional_checks(); -		unset($this->upload); - -		return true; -	} - -	/** -	* Performing additional checks -	*/ -	function additional_checks() -	{ -		global $user; - -		if (!$this->file_moved) -		{ -			return false; -		} - -		// Filesize is too big or it's 0 if it was larger than the maxsize in the upload form -		if ($this->upload->max_filesize && ($this->get('filesize') > $this->upload->max_filesize || $this->filesize == 0)) -		{ -			$max_filesize = get_formatted_filesize($this->upload->max_filesize, false); - -			$this->error[] = sprintf($user->lang[$this->upload->error_prefix . 'WRONG_FILESIZE'], $max_filesize['value'], $max_filesize['unit']); - -			return false; -		} - -		if (!$this->upload->valid_dimensions($this)) -		{ -			$this->error[] = $user->lang($this->upload->error_prefix . 'WRONG_SIZE', -				$user->lang('PIXELS', (int) $this->upload->min_width), -				$user->lang('PIXELS', (int) $this->upload->min_height), -				$user->lang('PIXELS', (int) $this->upload->max_width), -				$user->lang('PIXELS', (int) $this->upload->max_height), -				$user->lang('PIXELS', (int) $this->width), -				$user->lang('PIXELS', (int) $this->height)); - -			return false; -		} - -		return true; -	} -} - -/** -* Class for assigning error messages before a real filespec class can be assigned -*/ -class fileerror extends filespec -{ -	function fileerror($error_msg) -	{ -		$this->error[] = $error_msg; -	} -} - -/** -* File upload class -* Init class (all parameters optional and able to be set/overwritten separately) - scope is global and valid for all uploads -*/ -class fileupload -{ -	var $allowed_extensions = array(); -	var $disallowed_content = array('body', 'head', 'html', 'img', 'plaintext', 'a href', 'pre', 'script', 'table', 'title'); -	var $max_filesize = 0; -	var $min_width = 0; -	var $min_height = 0; -	var $max_width = 0; -	var $max_height = 0; -	var $error_prefix = ''; - -	/** @var int Timeout for remote upload */ -	var $upload_timeout = 6; - -	/** -	 * @var \phpbb\filesystem\filesystem_interface -	 */ -	protected $filesystem; - -	/** -	* Init file upload class. -	* -	* @param \phpbb\filesystem\filesystem_interface $filesystem -	* @param string $error_prefix Used error messages will get prefixed by this string -	* @param array $allowed_extensions Array of allowed extensions, for example array('jpg', 'jpeg', 'gif', 'png') -	* @param int $max_filesize Maximum filesize -	* @param int $min_width Minimum image width (only checked for images) -	* @param int $min_height Minimum image height (only checked for images) -	* @param int $max_width Maximum image width (only checked for images) -	* @param int $max_height Maximum image height (only checked for images) -	* @param bool|array $disallowed_content If enabled, the first 256 bytes of the file must not -	*										contain any of its values. Defaults to false. -	* -	*/ -	function fileupload(\phpbb\filesystem\filesystem_interface $filesystem, $error_prefix = '', $allowed_extensions = false, $max_filesize = false, $min_width = false, $min_height = false, $max_width = false, $max_height = false, $disallowed_content = false) -	{ -		$this->set_allowed_extensions($allowed_extensions); -		$this->set_max_filesize($max_filesize); -		$this->set_allowed_dimensions($min_width, $min_height, $max_width, $max_height); -		$this->set_error_prefix($error_prefix); -		$this->set_disallowed_content($disallowed_content); -		$this->filesystem = $filesystem; -	} - -	/** -	* Reset vars -	*/ -	function reset_vars() -	{ -		$this->max_filesize = 0; -		$this->min_width = $this->min_height = $this->max_width = $this->max_height = 0; -		$this->error_prefix = ''; -		$this->allowed_extensions = array(); -		$this->disallowed_content = array(); -	} - -	/** -	* Set allowed extensions -	*/ -	function set_allowed_extensions($allowed_extensions) -	{ -		if ($allowed_extensions !== false && is_array($allowed_extensions)) -		{ -			$this->allowed_extensions = $allowed_extensions; -		} -	} - -	/** -	* Set allowed dimensions -	*/ -	function set_allowed_dimensions($min_width, $min_height, $max_width, $max_height) -	{ -		$this->min_width = (int) $min_width; -		$this->min_height = (int) $min_height; -		$this->max_width = (int) $max_width; -		$this->max_height = (int) $max_height; -	} - -	/** -	* Set maximum allowed filesize -	*/ -	function set_max_filesize($max_filesize) -	{ -		if ($max_filesize !== false && (int) $max_filesize) -		{ -			$this->max_filesize = (int) $max_filesize; -		} -	} - -	/** -	* Set disallowed strings -	*/ -	function set_disallowed_content($disallowed_content) -	{ -		if ($disallowed_content !== false && is_array($disallowed_content)) -		{ -			$this->disallowed_content = array_diff($disallowed_content, array('')); -		} -	} - -	/** -	* Set error prefix -	*/ -	function set_error_prefix($error_prefix) -	{ -		$this->error_prefix = $error_prefix; -	} - -	/** -	* Form upload method -	* Upload file from users harddisk -	* -	* @param string $form_name Form name assigned to the file input field (if it is an array, the key has to be specified) -	* @param \phpbb\mimetype\guesser $mimetype_guesser Mimetype guesser -	* @param \phpbb\plupload\plupload $plupload The plupload object -	* -	* @return object $file Object "filespec" is returned, all further operations can be done with this object -	* @access public -	*/ -	function form_upload($form_name, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null) -	{ -		global $user, $request; - -		$upload = $request->file($form_name); -		unset($upload['local_mode']); - -		if ($plupload) -		{ -			$result = $plupload->handle_upload($form_name); -			if (is_array($result)) -			{ -				$upload = array_merge($upload, $result); -			} -		} - -		$file = new filespec($upload, $this, $this->filesystem, $mimetype_guesser, $plupload); - -		if ($file->init_error) -		{ -			$file->error[] = ''; -			return $file; -		} - -		// Error array filled? -		if (isset($upload['error'])) -		{ -			$error = $this->assign_internal_error($upload['error']); - -			if ($error !== false) -			{ -				$file->error[] = $error; -				return $file; -			} -		} - -		// Check if empty file got uploaded (not catched by is_uploaded_file) -		if (isset($upload['size']) && $upload['size'] == 0) -		{ -			$file->error[] = $user->lang[$this->error_prefix . 'EMPTY_FILEUPLOAD']; -			return $file; -		} - -		// PHP Upload filesize exceeded -		if ($file->get('filename') == 'none') -		{ -			$max_filesize = @ini_get('upload_max_filesize'); -			$unit = 'MB'; - -			if (!empty($max_filesize)) -			{ -				$unit = strtolower(substr($max_filesize, -1, 1)); -				$max_filesize = (int) $max_filesize; - -				$unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB'); -			} - -			$file->error[] = (empty($max_filesize)) ? $user->lang[$this->error_prefix . 'PHP_SIZE_NA'] : sprintf($user->lang[$this->error_prefix . 'PHP_SIZE_OVERRUN'], $max_filesize, $user->lang[$unit]); -			return $file; -		} - -		// Not correctly uploaded -		if (!$file->is_uploaded()) -		{ -			$file->error[] = $user->lang[$this->error_prefix . 'NOT_UPLOADED']; -			return $file; -		} - -		$this->common_checks($file); - -		return $file; -	} - -	/** -	* Move file from another location to phpBB -	*/ -	function local_upload($source_file, $filedata = false, \phpbb\mimetype\guesser $mimetype_guesser = null) -	{ -		global $user, $request; - -		$upload = array(); - -		$upload['local_mode'] = true; -		$upload['tmp_name'] = $source_file; - -		if ($filedata === false) -		{ -			$upload['name'] = utf8_basename($source_file); -			$upload['size'] = 0; -		} -		else -		{ -			$upload['name'] = $filedata['realname']; -			$upload['size'] = $filedata['size']; -			$upload['type'] = $filedata['type']; -		} - -		$file = new filespec($upload, $this, $this->filesystem, $mimetype_guesser); - -		if ($file->init_error) -		{ -			$file->error[] = ''; -			return $file; -		} - -		if (isset($upload['error'])) -		{ -			$error = $this->assign_internal_error($upload['error']); - -			if ($error !== false) -			{ -				$file->error[] = $error; -				return $file; -			} -		} - -		// PHP Upload filesize exceeded -		if ($file->get('filename') == 'none') -		{ -			$max_filesize = @ini_get('upload_max_filesize'); -			$unit = 'MB'; - -			if (!empty($max_filesize)) -			{ -				$unit = strtolower(substr($max_filesize, -1, 1)); -				$max_filesize = (int) $max_filesize; - -				$unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB'); -			} - -			$file->error[] = (empty($max_filesize)) ? $user->lang[$this->error_prefix . 'PHP_SIZE_NA'] : sprintf($user->lang[$this->error_prefix . 'PHP_SIZE_OVERRUN'], $max_filesize, $user->lang[$unit]); -			return $file; -		} - -		// Not correctly uploaded -		if (!$file->is_uploaded()) -		{ -			$file->error[] = $user->lang[$this->error_prefix . 'NOT_UPLOADED']; -			return $file; -		} - -		$this->common_checks($file); -		$request->overwrite('local', $upload, \phpbb\request\request_interface::FILES); - -		return $file; -	} - -	/** -	* Remote upload method -	* Uploads file from given url -	* -	* @param string $upload_url URL pointing to file to upload, for example http://www.foobar.com/example.gif -	* @param \phpbb\mimetype\guesser $mimetype_guesser Mimetype guesser -	* @return object $file Object "filespec" is returned, all further operations can be done with this object -	* @access public -	*/ -	function remote_upload($upload_url, \phpbb\mimetype\guesser $mimetype_guesser = null) -	{ -		global $user, $phpbb_root_path; - -		$upload_ary = array(); -		$upload_ary['local_mode'] = true; - -		if (!preg_match('#^(https?://).*?\.(' . implode('|', $this->allowed_extensions) . ')$#i', $upload_url, $match)) -		{ -			$file = new fileerror($user->lang[$this->error_prefix . 'URL_INVALID']); -			return $file; -		} - -		if (empty($match[2])) -		{ -			$file = new fileerror($user->lang[$this->error_prefix . 'URL_INVALID']); -			return $file; -		} - -		$url = parse_url($upload_url); - -		$host = $url['host']; -		$path = $url['path']; -		$port = (!empty($url['port'])) ? (int) $url['port'] : 80; - -		$upload_ary['type'] = 'application/octet-stream'; - -		$url['path'] = explode('.', $url['path']); -		$ext = array_pop($url['path']); - -		$url['path'] = implode('', $url['path']); -		$upload_ary['name'] = utf8_basename($url['path']) . (($ext) ? '.' . $ext : ''); -		$filename = $url['path']; -		$filesize = 0; - -		$remote_max_filesize = $this->max_filesize; -		if (!$remote_max_filesize) -		{ -			$max_filesize = @ini_get('upload_max_filesize'); - -			if (!empty($max_filesize)) -			{ -				$unit = strtolower(substr($max_filesize, -1, 1)); -				$remote_max_filesize = (int) $max_filesize; - -				switch ($unit) -				{ -					case 'g': -						$remote_max_filesize *= 1024; -					// no break -					case 'm': -						$remote_max_filesize *= 1024; -					// no break -					case 'k': -						$remote_max_filesize *= 1024; -					// no break -				} -			} -		} - -		$errno = 0; -		$errstr = ''; - -		if (!($fsock = @fsockopen($host, $port, $errno, $errstr))) -		{ -			$file = new fileerror($user->lang[$this->error_prefix . 'NOT_UPLOADED']); -			return $file; -		} - -		// Make sure $path not beginning with / -		if (strpos($path, '/') === 0) -		{ -			$path = substr($path, 1); -		} - -		fputs($fsock, 'GET /' . $path . " HTTP/1.1\r\n"); -		fputs($fsock, "HOST: " . $host . "\r\n"); -		fputs($fsock, "Connection: close\r\n\r\n"); - -		// Set a proper timeout for the socket -		socket_set_timeout($fsock, $this->upload_timeout); - -		$get_info = false; -		$data = ''; -		$length = false; -		$timer_stop = time() + $this->upload_timeout; - -		while ((!$length || $filesize < $length) && !@feof($fsock)) -		{ -			if ($get_info) -			{ -				if ($length) -				{ -					// Don't attempt to read past end of file if server indicated length -					$block = @fread($fsock, min($length - $filesize, 1024)); -				} -				else -				{ -					$block = @fread($fsock, 1024); -				} - -				$filesize += strlen($block); - -				if ($remote_max_filesize && $filesize > $remote_max_filesize) -				{ -					$max_filesize = get_formatted_filesize($remote_max_filesize, false); - -					$file = new fileerror(sprintf($user->lang[$this->error_prefix . 'WRONG_FILESIZE'], $max_filesize['value'], $max_filesize['unit'])); -					return $file; -				} - -				$data .= $block; -			} -			else -			{ -				$line = @fgets($fsock, 1024); - -				if ($line == "\r\n") -				{ -					$get_info = true; -				} -				else -				{ -					if (stripos($line, 'content-type: ') !== false) -					{ -						$upload_ary['type'] = rtrim(str_replace('content-type: ', '', strtolower($line))); -					} -					else if ($this->max_filesize && stripos($line, 'content-length: ') !== false) -					{ -						$length = (int) str_replace('content-length: ', '', strtolower($line)); - -						if ($remote_max_filesize && $length && $length > $remote_max_filesize) -						{ -							$max_filesize = get_formatted_filesize($remote_max_filesize, false); - -							$file = new fileerror(sprintf($user->lang[$this->error_prefix . 'WRONG_FILESIZE'], $max_filesize['value'], $max_filesize['unit'])); -							return $file; -						} -					} -					else if (stripos($line, '404 not found') !== false) -					{ -						$file = new fileerror($user->lang[$this->error_prefix . 'URL_NOT_FOUND']); -						return $file; -					} -				} -			} - -			$stream_meta_data = stream_get_meta_data($fsock); - -			// Cancel upload if we exceed timeout -			if (!empty($stream_meta_data['timed_out']) || time() >= $timer_stop) -			{ -				$file = new fileerror($user->lang[$this->error_prefix . 'REMOTE_UPLOAD_TIMEOUT']); -				return $file; -			} -		} -		@fclose($fsock); - -		if (empty($data)) -		{ -			$file = new fileerror($user->lang[$this->error_prefix . 'EMPTY_REMOTE_DATA']); -			return $file; -		} - -		$tmp_path = (!@ini_get('safe_mode') || strtolower(@ini_get('safe_mode')) == 'off') ? false : $phpbb_root_path . 'cache'; -		$filename = tempnam($tmp_path, unique_id() . '-'); - -		if (!($fp = @fopen($filename, 'wb'))) -		{ -			$file = new fileerror($user->lang[$this->error_prefix . 'NOT_UPLOADED']); -			return $file; -		} - -		$upload_ary['size'] = fwrite($fp, $data); -		fclose($fp); -		unset($data); - -		$upload_ary['tmp_name'] = $filename; - -		$file = new filespec($upload_ary, $this, $this->filesystem, $mimetype_guesser); -		$this->common_checks($file); - -		return $file; -	} - -	/** -	* Assign internal error -	* @access private -	*/ -	function assign_internal_error($errorcode) -	{ -		global $user; - -		switch ($errorcode) -		{ -			case 1: -				$max_filesize = @ini_get('upload_max_filesize'); -				$unit = 'MB'; - -				if (!empty($max_filesize)) -				{ -					$unit = strtolower(substr($max_filesize, -1, 1)); -					$max_filesize = (int) $max_filesize; - -					$unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB'); -				} - -				$error = (empty($max_filesize)) ? $user->lang[$this->error_prefix . 'PHP_SIZE_NA'] : sprintf($user->lang[$this->error_prefix . 'PHP_SIZE_OVERRUN'], $max_filesize, $user->lang[$unit]); -			break; - -			case 2: -				$max_filesize = get_formatted_filesize($this->max_filesize, false); - -				$error = sprintf($user->lang[$this->error_prefix . 'WRONG_FILESIZE'], $max_filesize['value'], $max_filesize['unit']); -			break; - -			case 3: -				$error = $user->lang[$this->error_prefix . 'PARTIAL_UPLOAD']; -			break; - -			case 4: -				$error = $user->lang[$this->error_prefix . 'NOT_UPLOADED']; -			break; - -			case 6: -				$error = 'Temporary folder could not be found. Please check your PHP installation.'; -			break; - -			default: -				$error = false; -			break; -		} - -		return $error; -	} - -	/** -	* Perform common checks -	*/ -	function common_checks(&$file) -	{ -		global $user; - -		// Filesize is too big or it's 0 if it was larger than the maxsize in the upload form -		if ($this->max_filesize && ($file->get('filesize') > $this->max_filesize || $file->get('filesize') == 0)) -		{ -			$max_filesize = get_formatted_filesize($this->max_filesize, false); - -			$file->error[] = sprintf($user->lang[$this->error_prefix . 'WRONG_FILESIZE'], $max_filesize['value'], $max_filesize['unit']); -		} - -		// check Filename -		if (preg_match("#[\\/:*?\"<>|]#i", $file->get('realname'))) -		{ -			$file->error[] = sprintf($user->lang[$this->error_prefix . 'INVALID_FILENAME'], $file->get('realname')); -		} - -		// Invalid Extension -		if (!$this->valid_extension($file)) -		{ -			$file->error[] = sprintf($user->lang[$this->error_prefix . 'DISALLOWED_EXTENSION'], $file->get('extension')); -		} - -		// MIME Sniffing -		if (!$this->valid_content($file)) -		{ -			$file->error[] = sprintf($user->lang[$this->error_prefix . 'DISALLOWED_CONTENT']); -		} -	} - -	/** -	* Check for allowed extension -	*/ -	function valid_extension(&$file) -	{ -		return (in_array($file->get('extension'), $this->allowed_extensions)) ? true : false; -	} - -	/** -	* Check for allowed dimension -	*/ -	function valid_dimensions(&$file) -	{ -		if (!$this->max_width && !$this->max_height && !$this->min_width && !$this->min_height) -		{ -			return true; -		} - -		if (($file->get('width') > $this->max_width && $this->max_width) || -			($file->get('height') > $this->max_height && $this->max_height) || -			($file->get('width') < $this->min_width && $this->min_width) || -			($file->get('height') < $this->min_height && $this->min_height)) -		{ -			return false; -		} - -		return true; -	} - -	/** -	* Check if form upload is valid -	*/ -	function is_valid($form_name) -	{ -		global $request; -		$upload = $request->file($form_name); - -		return (!empty($upload) && $upload['name'] !== 'none'); -	} - - -	/** -	* Check for bad content (IE mime-sniffing) -	*/ -	function valid_content(&$file) -	{ -		return ($file->check_content($this->disallowed_content)); -	} - -	/** -	* Get image type/extension mapping -	* -	* @return array Array containing the image types and their extensions -	*/ -	static public function image_types() -	{ -		$result = array( -			IMAGETYPE_GIF		=> array('gif'), -			IMAGETYPE_JPEG		=> array('jpg', 'jpeg'), -			IMAGETYPE_PNG		=> array('png'), -			IMAGETYPE_SWF		=> array('swf'), -			IMAGETYPE_PSD		=> array('psd'), -			IMAGETYPE_BMP		=> array('bmp'), -			IMAGETYPE_TIFF_II	=> array('tif', 'tiff'), -			IMAGETYPE_TIFF_MM	=> array('tif', 'tiff'), -			IMAGETYPE_JPC		=> array('jpg', 'jpeg'), -			IMAGETYPE_JP2		=> array('jpg', 'jpeg'), -			IMAGETYPE_JPX		=> array('jpg', 'jpeg'), -			IMAGETYPE_JB2		=> array('jpg', 'jpeg'), -			IMAGETYPE_IFF		=> array('iff'), -			IMAGETYPE_WBMP		=> array('wbmp'), -			IMAGETYPE_XBM		=> array('xbm'), -		); - -		if (defined('IMAGETYPE_SWC')) -		{ -			$result[IMAGETYPE_SWC] = array('swc'); -		} - -		return $result; -	} -} diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 853848d637..299467b5f3 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -205,8 +205,8 @@ $migrator->set_migrations($migrations);  // What is a safe limit of execution time? Half the max execution time should be safe.  //  No more than 15 seconds so the user isn't sitting and waiting for a very long time -$phpbb_ini = new \phpbb\php\ini(); -$safe_time_limit = min(15, ($phpbb_ini->get_int('max_execution_time') / 2)); +$phpbb_ini = new \bantu\IniGetWrapper\IniGetWrapper(); +$safe_time_limit = min(15, ($phpbb_ini->getNumeric('max_execution_time') / 2));  // While we're going to try limit this to half the max execution time,  //  we want to try and take additional measures to prevent hitting the diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php index bde9a91267..0390dbb2ac 100644 --- a/phpBB/language/en/common.php +++ b/phpBB/language/en/common.php @@ -105,11 +105,13 @@ $lang = array_merge($lang, array(  	'AVATAR_EMPTY_FILEUPLOAD'		=> 'The uploaded avatar file is empty.',  	'AVATAR_INVALID_FILENAME'		=> '%s is an invalid filename.',  	'AVATAR_NOT_UPLOADED'			=> 'Avatar could not be uploaded.', +	'AVATAR_NO_TEMP_DIR'			=> 'Temporary folder could not be found or is not writable.',  	'AVATAR_NO_SIZE'				=> 'The width or height of the linked avatar could not be determined. Please enter them manually.',  	'AVATAR_PARTIAL_UPLOAD'			=> 'The specified file was only partially uploaded.',  	'AVATAR_PHP_SIZE_NA'			=> 'The avatar’s filesize is too large.<br />The maximum allowed filesize set in php.ini could not be determined.',  	'AVATAR_PHP_SIZE_OVERRUN'		=> 'The avatar’s filesize is too large. The maximum allowed upload size is %1$d %2$s.<br />Please note this is set in php.ini and cannot be overridden.',  	'AVATAR_REMOTE_UPLOAD_TIMEOUT'		=> 'The specified avatar could not be uploaded because the request timed out.', +	'AVATAR_PHP_UPLOAD_STOPPED'		=> 'A PHP extension has stopped the file upload.',  	'AVATAR_URL_INVALID'			=> 'The URL you specified is invalid.',  	'AVATAR_URL_NOT_FOUND'			=> 'The file specified could not be found.',  	'AVATAR_WRONG_FILESIZE'			=> 'The avatar’s filesize must be between 0 and %1$d %2$s.', diff --git a/phpBB/language/en/posting.php b/phpBB/language/en/posting.php index 924395ed44..0e2d706f19 100644 --- a/phpBB/language/en/posting.php +++ b/phpBB/language/en/posting.php @@ -176,8 +176,10 @@ $lang = array_merge($lang, array(  	'NO_POLL_TITLE'				=> 'You have to enter a poll title.',  	'NO_POST'					=> 'The requested post does not exist.',  	'NO_POST_MODE'				=> 'No post mode specified.', +	'NO_TEMP_DIR'				=> 'Temporary folder could not be found or is not writable.',  	'PARTIAL_UPLOAD'			=> 'The uploaded file was only partially uploaded.', +	'PHP_UPLOAD_STOPPED'		=> 'A PHP extension has stopped the file upload.',  	'PHP_SIZE_NA'				=> 'The attachment’s file size is too large.<br />Could not determine the maximum size defined by PHP in php.ini.',  	'PHP_SIZE_OVERRUN'			=> 'The attachment’s file size is too large, the maximum upload size is %1$d %2$s.<br />Please note this is set in php.ini and cannot be overridden.',  	'PLACE_INLINE'				=> 'Place inline', diff --git a/phpBB/phpbb/avatar/driver/remote.php b/phpBB/phpbb/avatar/driver/remote.php index 90443c9b4e..0526b9184e 100644 --- a/phpBB/phpbb/avatar/driver/remote.php +++ b/phpBB/phpbb/avatar/driver/remote.php @@ -114,13 +114,8 @@ class remote extends \phpbb\avatar\driver\driver  			return false;  		} -		if (!class_exists('fileupload')) -		{ -			include($this->phpbb_root_path . 'includes/functions_upload.' . $this->php_ext); -		} - -		$types = \fileupload::image_types(); -		$extension = strtolower(\filespec::get_extension($url)); +		$types = \phpbb\files\upload::image_types(); +		$extension = strtolower(\phpbb\files\filespec::get_extension($url));  		// Check if this is actually an image  		if ($file_stream = @fopen($url, 'r')) diff --git a/phpBB/phpbb/avatar/driver/upload.php b/phpBB/phpbb/avatar/driver/upload.php index b31609b982..a0c23cb624 100644 --- a/phpBB/phpbb/avatar/driver/upload.php +++ b/phpBB/phpbb/avatar/driver/upload.php @@ -24,36 +24,36 @@ class upload extends \phpbb\avatar\driver\driver  	protected $filesystem;  	/** -	* @var \phpbb\mimetype\guesser -	*/ -	protected $mimetype_guesser; - -	/**  	* @var \phpbb\event\dispatcher_interface  	*/  	protected $dispatcher;  	/** +	 * @var \phpbb\files\factory +	 */ +	protected $files_factory; + +	/**  	* Construct a driver object  	*  	* @param \phpbb\config\config $config phpBB configuration  	* @param string $phpbb_root_path Path to the phpBB root  	* @param string $php_ext PHP file extension -	* @param \phpbb\filesystem\filesystem_interface phpBB filesystem helper +	* @param \phpbb\filesystem\filesystem_interface $filesystem phpBB filesystem helper  	* @param \phpbb\path_helper $path_helper phpBB path helper -	* @param \phpbb\mimetype\guesser $mimetype_guesser Mimetype guesser  	* @param \phpbb\event\dispatcher_interface $dispatcher phpBB Event dispatcher object +	* @param \phpbb\files\factory $files_factory File classes factory  	* @param \phpbb\cache\driver\driver_interface $cache Cache driver  	*/ -	public function __construct(\phpbb\config\config $config, $phpbb_root_path, $php_ext, \phpbb\filesystem\filesystem_interface $filesystem, \phpbb\path_helper $path_helper, \phpbb\mimetype\guesser $mimetype_guesser, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\cache\driver\driver_interface $cache = null) +	public function __construct(\phpbb\config\config $config, $phpbb_root_path, $php_ext, \phpbb\filesystem\filesystem_interface $filesystem, \phpbb\path_helper $path_helper, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\files\factory $files_factory, \phpbb\cache\driver\driver_interface $cache = null)  	{  		$this->config = $config;  		$this->phpbb_root_path = $phpbb_root_path;  		$this->php_ext = $php_ext;  		$this->filesystem = $filesystem;  		$this->path_helper = $path_helper; -		$this->mimetype_guesser = $mimetype_guesser;  		$this->dispatcher = $dispatcher; +		$this->files_factory = $files_factory;  		$this->cache = $cache;  	} @@ -99,19 +99,24 @@ class upload extends \phpbb\avatar\driver\driver  			return false;  		} -		if (!class_exists('fileupload')) -		{ -			include($this->phpbb_root_path . 'includes/functions_upload.' . $this->php_ext); -		} - -		$upload = new \fileupload($this->filesystem, 'AVATAR_', $this->allowed_extensions, $this->config['avatar_filesize'], $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], (isset($this->config['mime_triggers']) ? explode('|', $this->config['mime_triggers']) : false)); +		/** @var \phpbb\files\upload $upload */ +		$upload = $this->files_factory->get('upload') +			->set_error_prefix('AVATAR_') +			->set_allowed_extensions($this->allowed_extensions) +			->set_max_filesize($this->config['avatar_filesize']) +			->set_allowed_dimensions( +				$this->config['avatar_min_width'], +				$this->config['avatar_min_height'], +				$this->config['avatar_max_width'], +				$this->config['avatar_max_height']) +			->set_disallowed_content((isset($this->config['mime_triggers']) ? explode('|', $this->config['mime_triggers']) : false));  		$url = $request->variable('avatar_upload_url', '');  		$upload_file = $request->file('avatar_upload_file');  		if (!empty($upload_file['name']))  		{ -			$file = $upload->form_upload('avatar_upload_file', $this->mimetype_guesser); +			$file = $upload->handle_upload('files.types.form', 'avatar_upload_file');  		}  		else if (!empty($this->config['allow_avatar_remote_upload']) && !empty($url))  		{ @@ -141,7 +146,7 @@ class upload extends \phpbb\avatar\driver\driver  				return false;  			} -			$file = $upload->remote_upload($url, $this->mimetype_guesser); +			$file = $upload->handle_upload('files.types.remote', $url);  		}  		else  		{ diff --git a/phpBB/phpbb/files/factory.php b/phpBB/phpbb/files/factory.php new file mode 100644 index 0000000000..84b7cc9449 --- /dev/null +++ b/phpBB/phpbb/files/factory.php @@ -0,0 +1,58 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files; + +class factory +{ +	/** +	 * @var \Symfony\Component\DependencyInjection\ContainerInterface +	 */ +	private $container; + +	/** +	 * Constructor +	 * +	 * @param \Symfony\Component\DependencyInjection\ContainerInterface $container +	 */ +	public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container) +	{ +		$this->container = $container; +	} + +	/** +	 * Get files service +	 * +	 * @param string $name Service name +	 * +	 * @return object|bool Requested service or false if service could not be +	 *				found by the container +	 */ +	public function get($name) +	{ +		$service = false; + +		$name = (strpos($name, '.') === false) ? 'files.' . $name : $name; + +		try +		{ +			$service = $this->container->get($name); +		} +		catch (\Exception $e) +		{ +			// do nothing +		} + +		return $service; +	} +} diff --git a/phpBB/phpbb/files/filespec.php b/phpBB/phpbb/files/filespec.php new file mode 100644 index 0000000000..2ff2a92c83 --- /dev/null +++ b/phpBB/phpbb/files/filespec.php @@ -0,0 +1,584 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files; + +use phpbb\language\language; + +/** + * Responsible for holding all file relevant information, as well as doing file-specific operations. + * The {@link fileupload fileupload class} can be used to upload several files, each of them being this object to operate further on. + */ +class filespec +{ +	/** @var string File name */ +	protected $filename = ''; + +	/** @var string Real name of file */ +	protected $realname = ''; + +	/** @var string Upload name of file */ +	protected $uploadname = ''; + +	/** @var string Mimetype of file */ +	protected $mimetype = ''; + +	/** @var string File extension */ +	protected $extension = ''; + +	/** @var int File size */ +	protected $filesize = 0; + +	/** @var int Width of file */ +	protected $width = 0; + +	/** @var int Height of file */ +	protected $height = 0; + +	/** @var array Image info including type and size */ +	protected $image_info = array(); + +	/** @var string Destination file name */ +	protected $destination_file = ''; + +	/** @var string Destination file path */ +	protected $destination_path = ''; + +	/** @var bool Whether file was moved */ +	protected $file_moved = false; + +	/** @var bool Whether file is local */ +	protected $local = false; + +	/** @var bool Class initialization flag */ +	protected $class_initialized = false; + +	/** @var array Error array */ +	public $error = array(); + +	/** @var upload Instance of upload class  */ +	public $upload; + +	/** @var \phpbb\filesystem\filesystem_interface */ +	protected $filesystem; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper ini_get() wrapper class */ +	protected $php_ini; + +	/** @var \FastImageSize\FastImageSize */ +	protected $imagesize; + +	/** @var language Language class */ +	protected $language; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	/** @var \phpbb\plupload\plupload The plupload object */ +	protected $plupload; + +	/** @var \phpbb\mimetype\guesser phpBB Mimetype guesser */ +	protected $mimetype_guesser; + +	/** +	 * File upload class +	 * +	 * @param \phpbb\filesystem\filesystem_interface	$phpbb_filesystem Filesystem +	 * @param language					$language Language +	 * @param \bantu\IniGetWrapper\IniGetWrapper			$php_ini ini_get() wrapper +	 * @param \FastImageSize\FastImageSize $imagesize Imagesize class +	 * @param string					$phpbb_root_path phpBB root path +	 * @param \phpbb\mimetype\guesser	$mimetype_guesser Mime type guesser +	 * @param \phpbb\plupload\plupload	$plupload Plupload +	 */ +	public function __construct(\phpbb\filesystem\filesystem_interface $phpbb_filesystem, language $language, \bantu\IniGetWrapper\IniGetWrapper $php_ini, \FastImageSize\FastImageSize $imagesize, $phpbb_root_path, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null) +	{ +		$this->filesystem = $phpbb_filesystem; +		$this->language = $language; +		$this->php_ini = $php_ini; +		$this->imagesize = $imagesize; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->plupload = $plupload; +		$this->mimetype_guesser = $mimetype_guesser; +	} + +	/** +	 * Set upload ary +	 * +	 * @param array $upload_ary Upload ary +	 * +	 * @return filespec This instance of the filespec class +	 */ +	public function set_upload_ary($upload_ary) +	{ +		if (!isset($upload_ary) || !sizeof($upload_ary)) +		{ +			return $this; +		} + +		$this->class_initialized = true; +		$this->filename = $upload_ary['tmp_name']; +		$this->filesize = $upload_ary['size']; +		$name = (STRIP) ? stripslashes($upload_ary['name']) : $upload_ary['name']; +		$name = trim(utf8_basename($name)); +		$this->realname = $this->uploadname = $name; +		$this->mimetype = $upload_ary['type']; + +		// Opera adds the name to the mime type +		$this->mimetype	= (strpos($this->mimetype, '; name') !== false) ? str_replace(strstr($this->mimetype, '; name'), '', $this->mimetype) : $this->mimetype; + +		if (!$this->mimetype) +		{ +			$this->mimetype = 'application/octet-stream'; +		} + +		$this->extension = strtolower(self::get_extension($this->realname)); + +		// Try to get real filesize from temporary folder (not always working) ;) +		$this->filesize = ($this->get_filesize($this->filename)) ?: $this->filesize; + +		$this->width = $this->height = 0; +		$this->file_moved = false; + +		$this->local = (isset($upload_ary['local_mode'])) ? true : false; + +		return $this; +	} + +	/** +	 * Set the upload namespace +	 * +	 * @param upload $namespace Instance of upload class +	 * +	 * @return filespec This instance of the filespec class +	 */ +	public function set_upload_namespace($namespace) +	{ +		$this->upload = $namespace; + +		return $this; +	} + +	/** +	 * Check if class members were not properly initialised yet +	 * +	 * @return bool True if there was an init error, false if not +	 */ +	public function init_error() +	{ +		return !$this->class_initialized; +	} + +	/** +	 * Set error in error array +	 * +	 * @param mixed $error Content for error array +	 * +	 * @return \phpbb\files\filespec This instance of the filespec class +	 */ +	public function set_error($error) +	{ +		$this->error[] = $error; + +		return $this; +	} + +	/** +	 * Cleans destination filename +	 * +	 * @param string $mode Either real, unique, or unique_ext. Real creates a +	 *				realname, filtering some characters, lowering every +	 *				character. Unique creates a unique filename. +	 * @param string $prefix Prefix applied to filename +	 * @param string $user_id The user_id is only needed for when cleaning a user's avatar +	 */ +	public function clean_filename($mode = 'unique', $prefix = '', $user_id = '') +	{ +		if ($this->init_error()) +		{ +			return; +		} + +		switch ($mode) +		{ +			case 'real': +				// Remove every extension from filename (to not let the mime bug being exposed) +				if (strpos($this->realname, '.') !== false) +				{ +					$this->realname = substr($this->realname, 0, strpos($this->realname, '.')); +				} + +				// Replace any chars which may cause us problems with _ +				$bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|'); + +				$this->realname = rawurlencode(str_replace($bad_chars, '_', strtolower($this->realname))); +				$this->realname = preg_replace("/%(\w{2})/", '_', $this->realname); + +				$this->realname = $prefix . $this->realname . '.' . $this->extension; +			break; + +			case 'unique': +				$this->realname = $prefix . md5(unique_id()); +			break; + +			case 'avatar': +				$this->extension = strtolower($this->extension); +				$this->realname = $prefix . $user_id . '.' . $this->extension; + +			break; + +			case 'unique_ext': +			default: +				$this->realname = $prefix . md5(unique_id()) . '.' . $this->extension; +		} +	} + +	/** +	 * Get property from file object +	 * +	 * @param string $property Name of property +	 * +	 * @return mixed Content of property +	 */ +	public function get($property) +	{ +		if ($this->init_error() || !isset($this->$property)) +		{ +			return false; +		} + +		return $this->$property; +	} + +	/** +	 * Check if file is an image (mime type) +	 * +	 * @return bool true if it is an image, false if not +	 */ +	public function is_image() +	{ +		return (strpos($this->mimetype, 'image/') === 0); +	} + +	/** +	 * Check if the file got correctly uploaded +	 * +	 * @return bool true if it is a valid upload, false if not +	 */ +	public function is_uploaded() +	{ +		$is_plupload = $this->plupload && $this->plupload->is_active(); + +		if (!$this->local && !$is_plupload && !is_uploaded_file($this->filename)) +		{ +			return false; +		} + +		if (($this->local || $is_plupload) && !file_exists($this->filename)) +		{ +			return false; +		} + +		return true; +	} + +	/** +	 * Remove file +	 */ +	public function remove() +	{ +		if ($this->file_moved) +		{ +			@unlink($this->destination_file); +		} +	} + +	/** +	 * Get file extension +	 * +	 * @param string $filename Filename that needs to be checked +	 * +	 * @return string Extension of the supplied filename +	 */ +	static public function get_extension($filename) +	{ +		$filename = utf8_basename($filename); + +		if (strpos($filename, '.') === false) +		{ +			return ''; +		} + +		$filename = explode('.', $filename); +		return array_pop($filename); +	} + +	/** +	 * Get mime type +	 * +	 * @param string $filename Filename that needs to be checked +	 * @return string Mime type of supplied filename +	 */ +	public function get_mimetype($filename) +	{ +		if ($this->mimetype_guesser !== null) +		{ +			$mimetype = $this->mimetype_guesser->guess($filename, $this->uploadname); + +			if ($mimetype !== 'application/octet-stream') +			{ +				$this->mimetype = $mimetype; +			} +		} + +		return $this->mimetype; +	} + +	/** +	 * Get file size +	 * +	 * @param string $filename File name of file to check +	 * +	 * @return int File size +	 */ +	public function get_filesize($filename) +	{ +		return @filesize($filename); +	} + + +	/** +	 * Check the first 256 bytes for forbidden content +	 * +	 * @param array $disallowed_content Array containg disallowed content +	 * +	 * @return bool False if disallowed content found, true if not +	 */ +	public function check_content($disallowed_content) +	{ +		if (empty($disallowed_content)) +		{ +			return true; +		} + +		$fp = @fopen($this->filename, 'rb'); + +		if ($fp !== false) +		{ +			$ie_mime_relevant = fread($fp, 256); +			fclose($fp); +			foreach ($disallowed_content as $forbidden) +			{ +				if (stripos($ie_mime_relevant, '<' . $forbidden) !== false) +				{ +					return false; +				} +			} +		} +		return true; +	} + +	/** +	 * Move file to destination folder +	 * The phpbb_root_path variable will be applied to the destination path +	 * +	 * @param string $destination Destination path, for example $config['avatar_path'] +	 * @param bool $overwrite If set to true, an already existing file will be overwritten +	 * @param bool $skip_image_check If set to true, the check for the file to be a valid image is skipped +	 * @param string|bool $chmod Permission mask for chmodding the file after a successful move. +	 *				The mode entered here reflects the mode defined by {@link phpbb_chmod()} +	 * +	 * @return bool True if file was moved, false if not +	 * @access public +	 */ +	public function move_file($destination, $overwrite = false, $skip_image_check = false, $chmod = false) +	{ +		if (sizeof($this->error)) +		{ +			return false; +		} + +		$chmod = ($chmod === false) ? CHMOD_READ | CHMOD_WRITE : $chmod; + +		// We need to trust the admin in specifying valid upload directories and an attacker not being able to overwrite it... +		$this->destination_path = $this->phpbb_root_path . $destination; + +		// Check if the destination path exist... +		if (!file_exists($this->destination_path)) +		{ +			@unlink($this->filename); +			return false; +		} + +		$upload_mode = ($this->php_ini->getBool('open_basedir') || $this->php_ini->getBool('safe_mode')) ? 'move' : 'copy'; +		$upload_mode = ($this->local) ? 'local' : $upload_mode; +		$this->destination_file = $this->destination_path . '/' . utf8_basename($this->realname); + +		// Check if the file already exist, else there is something wrong... +		if (file_exists($this->destination_file) && !$overwrite) +		{ +			@unlink($this->filename); +			$this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file); +			$this->file_moved = false; +			return false; +		} +		else +		{ +			if (file_exists($this->destination_file)) +			{ +				@unlink($this->destination_file); +			} + +			switch ($upload_mode) +			{ +				case 'copy': + +					if (!@copy($this->filename, $this->destination_file)) +					{ +						if (!@move_uploaded_file($this->filename, $this->destination_file)) +						{ +							$this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file); +						} +					} + +				break; + +				case 'move': + +					if (!@move_uploaded_file($this->filename, $this->destination_file)) +					{ +						if (!@copy($this->filename, $this->destination_file)) +						{ +							$this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file); +						} +					} + +				break; + +				case 'local': + +					if (!@copy($this->filename, $this->destination_file)) +					{ +						$this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file); +					} + +				break; +			} + +			// Remove temporary filename +			@unlink($this->filename); + +			if (sizeof($this->error)) +			{ +				return false; +			} + +			try +			{ +				$this->filesystem->phpbb_chmod($this->destination_file, $chmod); +			} +			catch (\phpbb\filesystem\exception\filesystem_exception $e) +			{ +				// Do nothing +			} +		} + +		// Try to get real filesize from destination folder +		$this->filesize = ($this->get_filesize($this->destination_file)) ?: $this->filesize; + +		// Get mimetype of supplied file +		$this->mimetype = $this->get_mimetype($this->destination_file); + +		if ($this->is_image() && !$skip_image_check) +		{ +			$this->width = $this->height = 0; + +			$this->image_info = $this->imagesize->getImageSize($this->destination_file, $this->mimetype); + +			if ($this->image_info !== false) +			{ +				$this->width = $this->image_info['width']; +				$this->height = $this->image_info['height']; + +				// Check image type +				$types = upload::image_types(); + +				if (!isset($types[$this->image_info['type']]) || !in_array($this->extension, $types[$this->image_info['type']])) +				{ +					if (!isset($types[$this->image_info['type']])) +					{ +						$this->error[] = $this->language->lang('IMAGE_FILETYPE_INVALID', $this->image_info['type'], $this->mimetype); +					} +					else +					{ +						$this->error[] = $this->language->lang('IMAGE_FILETYPE_MISMATCH', $types[$this->image_info['type']][0], $this->extension); +					} +				} + +				// Make sure the dimensions match a valid image +				if (empty($this->width) || empty($this->height)) +				{ +					$this->error[] = $this->language->lang('ATTACHED_IMAGE_NOT_IMAGE'); +				} +			} +			else +			{ +				$this->error[] = $this->language->lang('UNABLE_GET_IMAGE_SIZE'); +			} +		} + +		$this->file_moved = true; +		$this->additional_checks(); +		unset($this->upload); + +		return true; +	} + +	/** +	 * Performing additional checks +	 * +	 * @return bool False if issue was found, true if not +	 */ +	public function additional_checks() +	{ +		if (!$this->file_moved) +		{ +			return false; +		} + +		// Filesize is too big or it's 0 if it was larger than the maxsize in the upload form +		if ($this->upload->max_filesize && ($this->get('filesize') > $this->upload->max_filesize || $this->filesize == 0)) +		{ +			$max_filesize = get_formatted_filesize($this->upload->max_filesize, false); + +			$this->error[] = $this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']); + +			return false; +		} + +		if (!$this->upload->valid_dimensions($this)) +		{ +			$this->error[] = $this->language->lang($this->upload->error_prefix . 'WRONG_SIZE', +				$this->language->lang('PIXELS', (int) $this->upload->min_width), +				$this->language->lang('PIXELS', (int) $this->upload->min_height), +				$this->language->lang('PIXELS', (int) $this->upload->max_width), +				$this->language->lang('PIXELS', (int) $this->upload->max_height), +				$this->language->lang('PIXELS', (int) $this->width), +				$this->language->lang('PIXELS', (int) $this->height)); + +			return false; +		} + +		return true; +	} +} diff --git a/phpBB/phpbb/files/types/base.php b/phpBB/phpbb/files/types/base.php new file mode 100644 index 0000000000..3313ad040b --- /dev/null +++ b/phpBB/phpbb/files/types/base.php @@ -0,0 +1,65 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files\types; + +abstract class base implements type_interface +{ +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper */ +	protected $php_ini; + +	/** @var \phpbb\files\upload */ +	protected $upload; + +	/** +	 * Check if upload exceeds maximum file size +	 * +	 * @param \phpbb\files\filespec $file Filespec object +	 * +	 * @return \phpbb\files\filespec Returns same filespec instance +	 */ +	public function check_upload_size($file) +	{ +		// PHP Upload filesize exceeded +		if ($file->get('filename') == 'none') +		{ +			$max_filesize = $this->php_ini->getString('upload_max_filesize'); +			$unit = 'MB'; + +			if (!empty($max_filesize)) +			{ +				$unit = strtolower(substr($max_filesize, -1, 1)); +				$max_filesize = (int) $max_filesize; + +				$unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB'); +			} + +			$file->error[] = (empty($max_filesize)) ? $this->language->lang($this->upload->error_prefix . 'PHP_SIZE_NA') : $this->language->lang($this->upload->error_prefix . 'PHP_SIZE_OVERRUN', $max_filesize, $this->language->lang($unit)); +		} + +		return $file; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function set_upload(\phpbb\files\upload $upload) +	{ +		$this->upload = $upload; + +		return $this; +	} +} diff --git a/phpBB/phpbb/files/types/form.php b/phpBB/phpbb/files/types/form.php new file mode 100644 index 0000000000..832f090c47 --- /dev/null +++ b/phpBB/phpbb/files/types/form.php @@ -0,0 +1,138 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files\types; + +use bantu\IniGetWrapper\IniGetWrapper; +use phpbb\files\factory; +use phpbb\files\filespec; +use phpbb\language\language; +use phpbb\plupload\plupload; +use phpbb\request\request_interface; + +class form extends base +{ +	/** @var factory Files factory */ +	protected $factory; + +	/** @var language */ +	protected $language; + +	/** @var IniGetWrapper */ +	protected $php_ini; + +	/** @var plupload */ +	protected $plupload; + +	/** @var request_interface */ +	protected $request; + +	/** @var \phpbb\files\upload */ +	protected $upload; + +	/** +	 * Construct a form upload type +	 * +	 * @param factory			$factory	Files factory +	 * @param language			$language	Language class +	 * @param IniGetWrapper		$php_ini	ini_get() wrapper +	 * @param plupload			$plupload	Plupload +	 * @param request_interface	$request	Request object +	 */ +	public function __construct(factory $factory, language $language, IniGetWrapper $php_ini, plupload $plupload, request_interface $request) +	{ +		$this->factory = $factory; +		$this->language = $language; +		$this->php_ini = $php_ini; +		$this->plupload = $plupload; +		$this->request = $request; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function upload() +	{ +		$args = func_get_args(); +		return $this->form_upload($args[0]); +	} + +	/** +	 * Form upload method +	 * Upload file from users harddisk +	 * +	 * @param string $form_name Form name assigned to the file input field (if it is an array, the key has to be specified) +	 * +	 * @return filespec $file Object "filespec" is returned, all further operations can be done with this object +	 * @access public +	 */ +	protected function form_upload($form_name) +	{ +		$upload = $this->request->file($form_name); +		unset($upload['local_mode']); + +		$result = $this->plupload->handle_upload($form_name); +		if (is_array($result)) +		{ +			$upload = array_merge($upload, $result); +		} + +		/** @var filespec $file */ +		$file = $this->factory->get('filespec') +			->set_upload_ary($upload) +			->set_upload_namespace($this->upload); + +		if ($file->init_error()) +		{ +			$file->error[] = ''; +			return $file; +		} + +		// Error array filled? +		if (isset($upload['error'])) +		{ +			$error = $this->upload->assign_internal_error($upload['error']); + +			if ($error !== false) +			{ +				$file->error[] = $error; +				return $file; +			} +		} + +		// Check if empty file got uploaded (not catched by is_uploaded_file) +		if (isset($upload['size']) && $upload['size'] == 0) +		{ +			$file->error[] = $this->language->lang($this->upload->error_prefix . 'EMPTY_FILEUPLOAD'); +			return $file; +		} + +		// PHP Upload file size check +		$file = $this->check_upload_size($file); +		if (sizeof($file->error)) +		{ +			return $file; +		} + +		// Not correctly uploaded +		if (!$file->is_uploaded()) +		{ +			$file->error[] = $this->language->lang($this->upload->error_prefix . 'NOT_UPLOADED'); +			return $file; +		} + +		$this->upload->common_checks($file); + +		return $file; +	} +} diff --git a/phpBB/phpbb/files/types/local.php b/phpBB/phpbb/files/types/local.php new file mode 100644 index 0000000000..7e9210b196 --- /dev/null +++ b/phpBB/phpbb/files/types/local.php @@ -0,0 +1,136 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files\types; + +use bantu\IniGetWrapper\IniGetWrapper; +use phpbb\files\factory; +use phpbb\files\filespec; +use phpbb\language\language; +use phpbb\request\request_interface; + +class local extends base +{ +	/** @var factory Files factory */ +	protected $factory; + +	/** @var language */ +	protected $language; + +	/** @var IniGetWrapper */ +	protected $php_ini; + +	/** @var request_interface */ +	protected $request; + +	/** @var \phpbb\files\upload */ +	protected $upload; + +	/** +	 * Construct a form upload type +	 * +	 * @param factory $factory Files factory +	 * @param language $language Language class +	 * @param IniGetWrapper $php_ini ini_get() wrapper +	 * @param request_interface $request Request object +	 */ +	public function __construct(factory $factory, language $language, IniGetWrapper $php_ini, request_interface $request) +	{ +		$this->factory = $factory; +		$this->language = $language; +		$this->php_ini = $php_ini; +		$this->request = $request; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function upload() +	{ +		$args = func_get_args(); +		return $this->local_upload($args[0], isset($args[1]) ? $args[1] : false); +	} + +	/** +	 * Move file from another location to phpBB +	 * +	 * @param string $source_file Filename of source file +	 * @param array|bool $filedata Array with filedata or false +	 * +	 * @return filespec Object "filespec" is returned, all further operations can be done with this object +	 */ +	protected function local_upload($source_file, $filedata = false) +	{ +		$upload = $this->get_upload_ary($source_file, $filedata); + +		/** @var filespec $file */ +		$file = $this->factory->get('filespec') +			->set_upload_ary($upload) +			->set_upload_namespace($this->upload); + +		if ($file->init_error()) +		{ +			$file->error[] = ''; +			return $file; +		} + +		// PHP Upload file size check +		$file = $this->check_upload_size($file); +		if (sizeof($file->error)) +		{ +			return $file; +		} + +		// Not correctly uploaded +		if (!$file->is_uploaded()) +		{ +			$file->error[] = $this->language->lang($this->upload->error_prefix . 'NOT_UPLOADED'); +			return $file; +		} + +		$this->upload->common_checks($file); +		$this->request->overwrite('local', $upload, request_interface::FILES); + +		return $file; +	} + +	/** +	 * Retrieve upload array +	 * +	 * @param string $source_file Source file name +	 * @param array $filedata File data array +	 * +	 * @return array Upload array +	 */ +	protected function get_upload_ary($source_file, $filedata) +	{ +		$upload = array(); + +		$upload['local_mode'] = true; +		$upload['tmp_name'] = $source_file; + +		if ($filedata === false) +		{ +			$upload['name'] = utf8_basename($source_file); +			$upload['size'] = 0; +		} +		else +		{ +			$upload['name'] = $filedata['realname']; +			$upload['size'] = $filedata['size']; +			$upload['type'] = $filedata['type']; +		} + +		return $upload; +	} +} diff --git a/phpBB/phpbb/files/types/remote.php b/phpBB/phpbb/files/types/remote.php new file mode 100644 index 0000000000..44feab0ece --- /dev/null +++ b/phpBB/phpbb/files/types/remote.php @@ -0,0 +1,260 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files\types; + +use bantu\IniGetWrapper\IniGetWrapper; +use phpbb\files\factory; +use phpbb\files\filespec; +use phpbb\language\language; +use phpbb\request\request_interface; + +class remote extends base +{ +	/** @var factory Files factory */ +	protected $factory; + +	/** @var language */ +	protected $language; + +	/** @var IniGetWrapper */ +	protected $php_ini; + +	/** @var request_interface */ +	protected $request; + +	/** @var \phpbb\files\upload */ +	protected $upload; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	/** +	 * Construct a form upload type +	 * +	 * @param factory $factory Files factory +	 * @param language $language Language class +	 * @param IniGetWrapper $php_ini ini_get() wrapper +	 * @param request_interface $request Request object +	 * @param string $phpbb_root_path phpBB root path +	 */ +	public function __construct(factory $factory, language $language, IniGetWrapper $php_ini, request_interface $request, $phpbb_root_path) +	{ +		$this->factory = $factory; +		$this->language = $language; +		$this->php_ini = $php_ini; +		$this->request = $request; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	/** +	 * {@inheritdoc} +	 */ +	public function upload() +	{ +		$args = func_get_args(); +		return $this->remote_upload($args[0]); +	} + +	/** +	 * Remote upload method +	 * Uploads file from given url +	 * +	 * @param string $upload_url URL pointing to file to upload, for example http://www.foobar.com/example.gif +	 * @return filespec $file Object "filespec" is returned, all further operations can be done with this object +	 * @access public +	 */ +	protected function remote_upload($upload_url) +	{ +		$upload_ary = array(); +		$upload_ary['local_mode'] = true; + +		if (!preg_match('#^(https?://).*?\.(' . implode('|', $this->upload->allowed_extensions) . ')$#i', $upload_url, $match)) +		{ +			return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'URL_INVALID')); +		} + +		$url = parse_url($upload_url); + +		$host = $url['host']; +		$path = $url['path']; +		$port = (!empty($url['port'])) ? (int) $url['port'] : 80; + +		$upload_ary['type'] = 'application/octet-stream'; + +		$url['path'] = explode('.', $url['path']); +		$ext = array_pop($url['path']); + +		$url['path'] = implode('', $url['path']); +		$upload_ary['name'] = utf8_basename($url['path']) . (($ext) ? '.' . $ext : ''); +		$filename = $url['path']; +		$filesize = 0; + +		$remote_max_filesize = $this->get_max_file_size(); + +		$errno = 0; +		$errstr = ''; + +		if (!($fsock = @fsockopen($host, $port, $errno, $errstr))) +		{ +			return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'NOT_UPLOADED')); +		} + +		// Make sure $path not beginning with / +		if (strpos($path, '/') === 0) +		{ +			$path = substr($path, 1); +		} + +		fputs($fsock, 'GET /' . $path . " HTTP/1.1\r\n"); +		fputs($fsock, "HOST: " . $host . "\r\n"); +		fputs($fsock, "Connection: close\r\n\r\n"); + +		// Set a proper timeout for the socket +		socket_set_timeout($fsock, $this->upload->upload_timeout); + +		$get_info = false; +		$data = ''; +		$length = false; +		$timer_stop = time() + $this->upload->upload_timeout; + +		while ((!$length || $filesize < $length) && !@feof($fsock)) +		{ +			if ($get_info) +			{ +				if ($length) +				{ +					// Don't attempt to read past end of file if server indicated length +					$block = @fread($fsock, min($length - $filesize, 1024)); +				} +				else +				{ +					$block = @fread($fsock, 1024); +				} + +				$filesize += strlen($block); + +				if ($remote_max_filesize && $filesize > $remote_max_filesize) +				{ +					$max_filesize = get_formatted_filesize($remote_max_filesize, false); + +					return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit'])); +				} + +				$data .= $block; +			} +			else +			{ +				$line = @fgets($fsock, 1024); + +				if ($line == "\r\n") +				{ +					$get_info = true; +				} +				else +				{ +					if (stripos($line, 'content-type: ') !== false) +					{ +						$upload_ary['type'] = rtrim(str_replace('content-type: ', '', strtolower($line))); +					} +					else if ($this->upload->max_filesize && stripos($line, 'content-length: ') !== false) +					{ +						$length = (int) str_replace('content-length: ', '', strtolower($line)); + +						if ($remote_max_filesize && $length && $length > $remote_max_filesize) +						{ +							$max_filesize = get_formatted_filesize($remote_max_filesize, false); + +							return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit'])); +						} +					} +					else if (stripos($line, '404 not found') !== false) +					{ +						return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'URL_NOT_FOUND'); +					} +				} +			} + +			$stream_meta_data = stream_get_meta_data($fsock); + +			// Cancel upload if we exceed timeout +			if (!empty($stream_meta_data['timed_out']) || time() >= $timer_stop) +			{ +				return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'REMOTE_UPLOAD_TIMEOUT'); +			} +		} +		@fclose($fsock); + +		if (empty($data)) +		{ +			return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'EMPTY_REMOTE_DATA'); +		} + +		$tmp_path = (!$this->php_ini->getBool('safe_mode')) ? false : $this->phpbb_root_path . 'cache'; +		$filename = tempnam($tmp_path, unique_id() . '-'); + +		if (!($fp = @fopen($filename, 'wb'))) +		{ +			return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'NOT_UPLOADED'); +		} + +		$upload_ary['size'] = fwrite($fp, $data); +		fclose($fp); +		unset($data); + +		$upload_ary['tmp_name'] = $filename; + +		/** @var filespec $file */ +		$file = $this->factory->get('filespec') +			->set_upload_ary($upload_ary) +			->set_upload_namespace($this->upload); +		$this->upload->common_checks($file); + +		return $file; +	} + +	/** +	 * Get maximum file size for remote uploads +	 * +	 * @return int Maximum file size +	 */ +	protected function get_max_file_size() +	{ +		$max_file_size = $this->upload->max_filesize; +		if (!$max_file_size) +		{ +			$max_file_size = $this->php_ini->getString('upload_max_filesize'); + +			if (!empty($max_file_size)) +			{ +				$unit = strtolower(substr($max_file_size, -1, 1)); +				$max_file_size = (int) $max_file_size; + +				switch ($unit) +				{ +					case 'g': +						$max_file_size *= 1024; +					// no break +					case 'm': +						$max_file_size *= 1024; +					// no break +					case 'k': +						$max_file_size *= 1024; +					// no break +				} +			} +		} + +		return $max_file_size; +	} +} diff --git a/phpBB/phpbb/files/types/type_interface.php b/phpBB/phpbb/files/types/type_interface.php new file mode 100644 index 0000000000..e07078349a --- /dev/null +++ b/phpBB/phpbb/files/types/type_interface.php @@ -0,0 +1,38 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files\types; + +use phpbb\files\upload; + +interface type_interface +{ +	/** +	 * Handle upload for upload types. Arguments passed to this method will be +	 * handled by the upload type classes themselves. +	 * +	 * @return \phpbb\files\filespec|bool Filespec instance if upload is +	 *                                    successful or false if not +	 */ +	public function upload(); + +	/** +	 * Set upload instance +	 * Needs to be executed before every upload. +	 * +	 * @param upload $upload Upload instance +	 * +	 * @return type_interface Returns itself +	 */ +	public function set_upload(upload $upload); +} diff --git a/phpBB/phpbb/files/upload.php b/phpBB/phpbb/files/upload.php new file mode 100644 index 0000000000..e011e714e5 --- /dev/null +++ b/phpBB/phpbb/files/upload.php @@ -0,0 +1,395 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files; + +use phpbb\filesystem\filesystem_interface; +use phpbb\language\language; +use phpbb\request\request_interface; + +/** + * File upload class + * Init class (all parameters optional and able to be set/overwritten separately) - scope is global and valid for all uploads + */ +class upload +{ +	/** @var array Allowed file extensions */ +	public $allowed_extensions = array(); + +	/** @var array Disallowed content */ +	protected $disallowed_content = array('body', 'head', 'html', 'img', 'plaintext', 'a href', 'pre', 'script', 'table', 'title'); + +	/** @var int Maximum filesize */ +	public $max_filesize = 0; + +	/** @var int Minimum width of images */ +	public $min_width = 0; + +	/** @var int Minimum height of images */ +	public $min_height = 0; + +	/** @var int Maximum width of images */ +	public $max_width = 0; + +	/** @var int Maximum height of images */ +	public $max_height = 0; + +	/** @var string Prefix for language variables of errors */ +	public $error_prefix = ''; + +	/** @var int Timeout for remote upload */ +	public $upload_timeout = 6; + +	/** @var filesystem_interface */ +	protected $filesystem; + +	/** @var \phpbb\files\factory Files factory */ +	protected $factory; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper ini_get() wrapper */ +	protected $php_ini; + +	/** @var \phpbb\language\language Language class */ +	protected $language; + +	/** @var request_interface Request class */ +	protected $request; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	/** +	 * Init file upload class. +	 * +	 * @param filesystem_interface $filesystem +	 * @param factory $factory Files factory +	 * @param language $language Language class +	 * @param \bantu\IniGetWrapper\IniGetWrapper $php_ini ini_get() wrapper +	 * @param request_interface $request Request class +	 * @param string $phpbb_root_path phpBB root path +	 */ +	public function __construct(filesystem_interface $filesystem, factory $factory, language $language, \bantu\IniGetWrapper\IniGetWrapper $php_ini, request_interface $request, $phpbb_root_path) +	{ +		$this->filesystem = $filesystem; +		$this->factory = $factory; +		$this->language = $language; +		$this->php_ini = $php_ini; +		$this->request = $request; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	/** +	 * Reset vars +	 */ +	public function reset_vars() +	{ +		$this->max_filesize = 0; +		$this->min_width = $this->min_height = $this->max_width = $this->max_height = 0; +		$this->error_prefix = ''; +		$this->allowed_extensions = array(); +		$this->disallowed_content = array(); +	} + +	/** +	 * Set allowed extensions +	 * +	 * @param array $allowed_extensions Allowed file extensions +	 * +	 * @return \phpbb\files\upload This instance of upload +	 */ +	public function set_allowed_extensions($allowed_extensions) +	{ +		if ($allowed_extensions !== false && is_array($allowed_extensions)) +		{ +			$this->allowed_extensions = $allowed_extensions; +		} + +		return $this; +	} + +	/** +	 * Set allowed dimensions +	 * +	 * @param int $min_width Minimum image width +	 * @param int $min_height Minimum image height +	 * @param int $max_width Maximum image width +	 * @param int $max_height Maximum image height +	 * +	 * @return \phpbb\files\upload This instance of upload +	 */ +	public function set_allowed_dimensions($min_width, $min_height, $max_width, $max_height) +	{ +		$this->min_width = (int) $min_width; +		$this->min_height = (int) $min_height; +		$this->max_width = (int) $max_width; +		$this->max_height = (int) $max_height; + +		return $this; +	} + +	/** +	 * Set maximum allowed file size +	 * +	 * @param int $max_filesize Maximum file size +	 * +	 * @return \phpbb\files\upload This instance of upload +	 */ +	public function set_max_filesize($max_filesize) +	{ +		if ($max_filesize !== false && (int) $max_filesize) +		{ +			$this->max_filesize = (int) $max_filesize; +		} + +		return $this; +	} + +	/** +	 * Set disallowed strings +	 * +	 * @param array $disallowed_content Disallowed content +	 * +	 * @return \phpbb\files\upload This instance of upload +	 */ +	public function set_disallowed_content($disallowed_content) +	{ +		if ($disallowed_content !== false && is_array($disallowed_content)) +		{ +			$this->disallowed_content = array_diff($disallowed_content, array('')); +		} + +		return $this; +	} + +	/** +	 * Set error prefix +	 * +	 * @param string $error_prefix Prefix for language variables of errors +	 * +	 * @return \phpbb\files\upload This instance of upload +	 */ +	public function set_error_prefix($error_prefix) +	{ +		$this->error_prefix = $error_prefix; + +		return $this; +	} + +	/** +	 * Handle upload based on type +	 * +	 * @param string $type Upload type +	 * +	 * @return \phpbb\files\filespec|bool A filespec instance if upload was +	 *		successful, false if there were issues or the type is not supported +	 */ +	public function handle_upload($type) +	{ +		$args = func_get_args(); +		array_shift($args); +		$type_class = $this->factory->get($type) +			->set_upload($this); + +		return (is_object($type_class)) ? call_user_func_array(array($type_class, 'upload'), $args) : false; +	} + +	/** +	 * Assign internal error +	 * +	 * @param string $errorcode Error code to assign +	 * +	 * @return string Error string +	 * @access public +	 */ +	public function assign_internal_error($errorcode) +	{ +		switch ($errorcode) +		{ +			case UPLOAD_ERR_INI_SIZE: +				$max_filesize = $this->php_ini->getString('upload_max_filesize'); +				$unit = 'MB'; + +				if (!empty($max_filesize)) +				{ +					$unit = strtolower(substr($max_filesize, -1, 1)); +					$max_filesize = (int) $max_filesize; + +					$unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB'); +				} + +				$error = (empty($max_filesize)) ? $this->language->lang($this->error_prefix . 'PHP_SIZE_NA') : $this->language->lang($this->error_prefix . 'PHP_SIZE_OVERRUN', $max_filesize, $this->language->lang($unit)); +			break; + +			case UPLOAD_ERR_FORM_SIZE: +				$max_filesize = get_formatted_filesize($this->max_filesize, false); + +				$error = $this->language->lang($this->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']); +			break; + +			case UPLOAD_ERR_PARTIAL: +				$error = $this->language->lang($this->error_prefix . 'PARTIAL_UPLOAD'); +			break; + +			case UPLOAD_ERR_NO_FILE: +				$error = $this->language->lang($this->error_prefix . 'NOT_UPLOADED'); +			break; + +			case UPLOAD_ERR_NO_TMP_DIR: +			case UPLOAD_ERR_CANT_WRITE: +				$error = $this->language->lang($this->error_prefix . 'NO_TEMP_DIR'); +			break; + +			case UPLOAD_ERR_EXTENSION: +				$error = $this->language->lang($this->error_prefix . 'PHP_UPLOAD_STOPPED'); +			break; + +			default: +				$error = false; +			break; +		} + +		return $error; +	} + +	/** +	 * Perform common file checks +	 * +	 * @param filespec $file Instance of filespec class +	 */ +	public function common_checks(&$file) +	{ +		// Filesize is too big or it's 0 if it was larger than the maxsize in the upload form +		if ($this->max_filesize && ($file->get('filesize') > $this->max_filesize || $file->get('filesize') == 0)) +		{ +			$max_filesize = get_formatted_filesize($this->max_filesize, false); + +			$file->error[] = $this->language->lang($this->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']); +		} + +		// check Filename +		if (preg_match("#[\\/:*?\"<>|]#i", $file->get('realname'))) +		{ +			$file->error[] = $this->language->lang($this->error_prefix . 'INVALID_FILENAME', $file->get('realname')); +		} + +		// Invalid Extension +		if (!$this->valid_extension($file)) +		{ +			$file->error[] = $this->language->lang($this->error_prefix . 'DISALLOWED_EXTENSION', $file->get('extension')); +		} + +		// MIME Sniffing +		if (!$this->valid_content($file)) +		{ +			$file->error[] = $this->language->lang($this->error_prefix . 'DISALLOWED_CONTENT'); +		} +	} + +	/** +	 * Check for allowed extension +	 * +	 * @param filespec $file Instance of filespec class +	 * +	 * @return bool True if extension is allowed, false if not +	 */ +	public function valid_extension(&$file) +	{ +		return (in_array($file->get('extension'), $this->allowed_extensions)) ? true : false; +	} + +	/** +	 * Check for allowed dimension +	 * +	 * @param filespec $file Instance of filespec class +	 * +	 * @return bool True if dimensions are valid or no constraints set, false +	 *			if not +	 */ +	public function valid_dimensions(&$file) +	{ +		if (!$this->max_width && !$this->max_height && !$this->min_width && !$this->min_height) +		{ +			return true; +		} + +		if (($file->get('width') > $this->max_width && $this->max_width) || +			($file->get('height') > $this->max_height && $this->max_height) || +			($file->get('width') < $this->min_width && $this->min_width) || +			($file->get('height') < $this->min_height && $this->min_height)) +		{ +			return false; +		} + +		return true; +	} + +	/** +	 * Check if form upload is valid +	 * +	 * @param string $form_name Name of form +	 * +	 * @return bool True if form upload is valid, false if not +	 */ +	public function is_valid($form_name) +	{ +		$upload = $this->request->file($form_name); + +		return (!empty($upload) && $upload['name'] !== 'none'); +	} + + +	/** +	 * Check for bad content (IE mime-sniffing) +	 * +	 * @param filespec $file Instance of filespec class +	 * +	 * @return bool True if content is valid, false if not +	 */ +	public function valid_content(&$file) +	{ +		return ($file->check_content($this->disallowed_content)); +	} + +	/** +	 * Get image type/extension mapping +	 * +	 * @return array Array containing the image types and their extensions +	 */ +	static public function image_types() +	{ +		$result = array( +			IMAGETYPE_GIF		=> array('gif'), +			IMAGETYPE_JPEG		=> array('jpg', 'jpeg'), +			IMAGETYPE_PNG		=> array('png'), +			IMAGETYPE_SWF		=> array('swf'), +			IMAGETYPE_PSD		=> array('psd'), +			IMAGETYPE_BMP		=> array('bmp'), +			IMAGETYPE_TIFF_II	=> array('tif', 'tiff'), +			IMAGETYPE_TIFF_MM	=> array('tif', 'tiff'), +			IMAGETYPE_JPC		=> array('jpg', 'jpeg'), +			IMAGETYPE_JP2		=> array('jpg', 'jpeg'), +			IMAGETYPE_JPX		=> array('jpg', 'jpeg'), +			IMAGETYPE_JB2		=> array('jpg', 'jpeg'), +			IMAGETYPE_IFF		=> array('iff'), +			IMAGETYPE_WBMP		=> array('wbmp'), +			IMAGETYPE_XBM		=> array('xbm'), +		); + +		if (defined('IMAGETYPE_SWC')) +		{ +			$result[IMAGETYPE_SWC] = array('swc'); +		} + +		return $result; +	} +} diff --git a/phpBB/phpbb/install/helper/config.php b/phpBB/phpbb/install/helper/config.php index b0480e7e5b..d5653f1924 100644 --- a/phpBB/phpbb/install/helper/config.php +++ b/phpBB/phpbb/install/helper/config.php @@ -41,7 +41,7 @@ class config  	protected $install_config_file;  	/** -	 * @var \phpbb\php\ini +	 * @var \bantu\IniGetWrapper\IniGetWrapper  	 */  	protected $php_ini; @@ -83,7 +83,7 @@ class config  	/**  	 * Constructor  	 */ -	public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, \phpbb\php\ini $php_ini, $phpbb_root_path) +	public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, \bantu\IniGetWrapper\IniGetWrapper $php_ini, $phpbb_root_path)  	{  		$this->filesystem		= $filesystem;  		$this->php_ini			= $php_ini; @@ -373,7 +373,7 @@ class config  	protected function setup_system_data()  	{  		// Query maximum runtime from php.ini -		$execution_time = $this->php_ini->get_int('max_execution_time'); +		$execution_time = $this->php_ini->getNumeric('max_execution_time');  		$execution_time = min(15, $execution_time / 2);  		$this->system_data['max_execution_time'] = $execution_time; @@ -381,6 +381,6 @@ class config  		$this->system_data['start_time'] = time();  		// Get memory limit -		$this->system_data['memory_limit'] = $this->php_ini->get_bytes('memory_limit'); +		$this->system_data['memory_limit'] = $this->php_ini->getBytes('memory_limit');  	}  } diff --git a/phpBB/phpbb/plupload/plupload.php b/phpBB/phpbb/plupload/plupload.php index ca78167ec0..0e67ee209b 100644 --- a/phpBB/phpbb/plupload/plupload.php +++ b/phpBB/phpbb/plupload/plupload.php @@ -39,7 +39,7 @@ class plupload  	protected $user;  	/** -	* @var \phpbb\php\ini +	* @var \bantu\IniGetWrapper\IniGetWrapper  	*/  	protected $php_ini; @@ -67,10 +67,10 @@ class plupload  	* @param \phpbb\config\config $config  	* @param \phpbb\request\request_interface $request  	* @param \phpbb\user $user -	* @param \phpbb\php\ini $php_ini +	* @param \bantu\IniGetWrapper\IniGetWrapper $php_ini  	* @param \phpbb\mimetype\guesser $mimetype_guesser  	*/ -	public function __construct($phpbb_root_path, \phpbb\config\config $config, \phpbb\request\request_interface $request, \phpbb\user $user, \phpbb\php\ini $php_ini, \phpbb\mimetype\guesser $mimetype_guesser) +	public function __construct($phpbb_root_path, \phpbb\config\config $config, \phpbb\request\request_interface $request, \phpbb\user $user, \bantu\IniGetWrapper\IniGetWrapper $php_ini, \phpbb\mimetype\guesser $mimetype_guesser)  	{  		$this->phpbb_root_path = $phpbb_root_path;  		$this->config = $config; @@ -284,9 +284,9 @@ class plupload  	public function get_chunk_size()  	{  		$max = min( -			$this->php_ini->get_bytes('upload_max_filesize'), -			$this->php_ini->get_bytes('post_max_size'), -			max(1, $this->php_ini->get_bytes('memory_limit')), +			$this->php_ini->getBytes('upload_max_filesize'), +			$this->php_ini->getBytes('post_max_size'), +			max(1, $this->php_ini->getBytes('memory_limit')),  			$this->config['max_filesize']  		); @@ -303,7 +303,7 @@ class plupload  			$this->temporary_directory,  			$this->config['plupload_salt'],  			md5($file_name), -			\filespec::get_extension($file_name) +			\phpbb\files\filespec::get_extension($file_name)  		);  	} diff --git a/tests/avatar/manager_test.php b/tests/avatar/manager_test.php index 9804869c81..9003f72de2 100644 --- a/tests/avatar/manager_test.php +++ b/tests/avatar/manager_test.php @@ -73,6 +73,8 @@ class phpbb_avatar_manager_test extends \phpbb_database_test_case  			->will($this->returnValue('avatar.driver.barfoo'));  		$avatar_drivers = array($this->avatar_foobar, $this->avatar_barfoo); +		$files_factory = new \phpbb\files\factory($phpbb_container); +  		foreach ($this->avatar_drivers() as $driver)  		{  			if ($driver !== 'upload') @@ -81,7 +83,7 @@ class phpbb_avatar_manager_test extends \phpbb_database_test_case  			}  			else  			{ -				$cur_avatar = $this->getMock('\phpbb\avatar\driver\\' . $driver, array('get_name'), array($this->config, $phpbb_root_path, $phpEx, $filesystem, $path_helper, $guesser, $dispatcher, $cache)); +				$cur_avatar = $this->getMock('\phpbb\avatar\driver\\' . $driver, array('get_name'), array($this->config, $phpbb_root_path, $phpEx, $filesystem, $path_helper, $dispatcher, $files_factory, $cache));  			}  			$cur_avatar->expects($this->any())  				->method('get_name') diff --git a/tests/cache/apc_driver_test.php b/tests/cache/apc_driver_test.php index 10130b465b..706f274448 100644 --- a/tests/cache/apc_driver_test.php +++ b/tests/cache/apc_driver_test.php @@ -34,14 +34,14 @@ class phpbb_cache_apc_driver_test extends phpbb_cache_common_test_case  			self::markTestSkipped('APC extension is not loaded');  		} -		$php_ini = new \phpbb\php\ini; +		$php_ini = new \bantu\IniGetWrapper\IniGetWrapper; -		if (!$php_ini->get_bool('apc.enabled')) +		if (!$php_ini->getBool('apc.enabled'))  		{  			self::markTestSkipped('APC is not enabled. Make sure apc.enabled=1 in php.ini');  		} -		if (PHP_SAPI == 'cli' && !$php_ini->get_bool('apc.enable_cli')) +		if (PHP_SAPI == 'cli' && !$php_ini->getBool('apc.enable_cli'))  		{  			self::markTestSkipped('APC is not enabled for CLI. Set apc.enable_cli=1 in php.ini');  		} diff --git a/tests/files/type_foo.php b/tests/files/type_foo.php new file mode 100644 index 0000000000..20eddfcbc4 --- /dev/null +++ b/tests/files/type_foo.php @@ -0,0 +1,31 @@ +<?php
 +/**
 + *
 + * This file is part of the phpBB Forum Software package.
 + *
 + * @copyright (c) phpBB Limited <https://www.phpbb.com>
 + * @license GNU General Public License, version 2 (GPL-2.0)
 + *
 + * For full copyright and license information, please see
 + * the docs/CREDITS.txt file.
 + *
 + */
 +
 +namespace phpbb\files\types;
 +
 +class foo extends \phpbb\files\types\remote
 +{
 +	static public $tempnam_path;
 +}
 +
 +function tempnam($one, $two)
 +{
 +	if (empty(foo::$tempnam_path))
 +	{
 +		return \tempnam($one, $two);
 +	}
 +	else
 +	{
 +		return foo::$tempnam_path;
 +	}
 +}
 diff --git a/tests/files/types_base_test.php b/tests/files/types_base_test.php new file mode 100644 index 0000000000..e630bf8c48 --- /dev/null +++ b/tests/files/types_base_test.php @@ -0,0 +1,93 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_files_types_base_test extends phpbb_test_case +{ +	private $path; + +	private $filesystem; + +	/** @var \Symfony\Component\DependencyInjection\ContainerInterface */ +	protected $container; + +	/** @var \phpbb\files\factory */ +	protected $factory; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper */ +	protected $php_ini; + +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var \phpbb\request\request_interface */ +	protected $request; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	protected function setUp() +	{ +		global $phpbb_root_path, $phpEx; + +		$this->request = $this->getMock('\phpbb\request\request'); + +		$this->filesystem = new \phpbb\filesystem\filesystem(); +		$this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); +		$this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + +		$this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); +		$this->container->set('files.filespec', new \phpbb\files\filespec( +			$this->filesystem, +			$this->language, +			$this->php_ini, +			new \FastImageSize\FastImageSize(), +			$phpbb_root_path, +			new \phpbb\mimetype\guesser(array( +				'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), +			)))); +		$this->factory = new \phpbb\files\factory($this->container); + +		$this->path = __DIR__ . '/fixture/'; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	public function data_check_upload_size() +	{ +		return array( +			array('foo', '500KB', array()), +			array('none', '500KB', array('PHP_SIZE_OVERRUN')), +			array('none', '', array('PHP_SIZE_NA')), +		); +	} + +	/** +	 * @dataProvider data_check_upload_size +	 */ +	public function test_check_upload_size($filename, $max_filesize, $expected) +	{ +		$php_ini = $this->getMock('\bantu\IniGetWrapper\IniGetWrapper'); +		$php_ini->expects($this->any()) +			->method('getString') +			->willReturn($max_filesize); +		$type_form = new \phpbb\files\types\local($this->factory, $this->language, $php_ini, $this->request); +		$file = $this->getMockBuilder('\phpbb\files\filespec') +			->disableOriginalConstructor() +			->getMock(); +		$file->expects($this->any()) +			->method('get') +			->willReturn($filename); +		$type_form->check_upload_size($file); + +		$this->assertSame($expected, $file->error); +	} +} diff --git a/tests/files/types_form_test.php b/tests/files/types_form_test.php new file mode 100644 index 0000000000..efcece49d1 --- /dev/null +++ b/tests/files/types_form_test.php @@ -0,0 +1,174 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + +class phpbb_files_types_form_test extends phpbb_test_case +{ +	private $path; + +	private $filesystem; + +	/** @var \Symfony\Component\DependencyInjection\ContainerInterface */ +	protected $container; + +	/** @var \phpbb\files\factory */ +	protected $factory; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper */ +	protected $php_ini; + +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var \phpbb\request\request_interface */ +	protected $request; + +	/** @var \phpbb\plupload\plupload */ +	protected $plupload; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	protected function setUp() +	{ +		global $phpbb_root_path, $phpEx; + +		$this->request = $this->getMock('\phpbb\request\request'); +		$this->request->expects($this->any()) +			->method('file') +			->willReturn(array()); + +		$this->filesystem = new \phpbb\filesystem\filesystem(); +		$this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); +		$this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + +		$this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); +		$this->container->set('files.filespec', new \phpbb\files\filespec( +			$this->filesystem, +			$this->language, +			$this->php_ini, +			new \FastImageSize\FastImageSize(), +			$phpbb_root_path, +			new \phpbb\mimetype\guesser(array( +				'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), +			)))); +		$this->factory = new \phpbb\files\factory($this->container); +		$this->plupload = $this->getMockBuilder('\phpbb\plupload\plupload') +			->disableOriginalConstructor() +			->getMock(); +		$this->plupload->expects($this->any()) +			->method('handle_upload') +			->willReturn(array()); + +		$this->path = __DIR__ . '/fixture/'; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	public function data_upload_form() +	{ +		return array( +			array( +				array(), +				array(''), +			), +			array( +				array( +					'tmp_name'		=> 'foo', +					'name'			=> 'foo', +					'size'			=> 500, +					'type'			=> 'image/png', +					'error'			=> UPLOAD_ERR_PARTIAL, +				), +				array('PARTIAL_UPLOAD'), +			), +			array( +				array( +					'tmp_name'		=> 'foo', +					'name'			=> 'foo', +					'size'			=> 500, +					'type'			=> 'image/png', +					'error'			=> -9, +				), +				array('NOT_UPLOADED'), +			), +			array( +				array( +					'tmp_name'		=> 'foo', +					'name'			=> 'foo', +					'size'			=> 0, +					'type'			=> 'image/png', +				), +				array('EMPTY_FILEUPLOAD'), +			), +			array( +				array( +					'tmp_name'		=> 'none', +					'name'			=> 'none', +					'size'			=> 50, +					'type'			=> 'image/png', +				), +				array('PHP_SIZE_OVERRUN'), +			), +			array( +				array( +					'tmp_name'		=> 'tests/upload/fixture/png', +					'name'			=> 'foo.png', +					'size'			=> 500, +					'type'			=> 'image/png', +					'local_mode'	=> true, +				), +				array(), +				array('local_mode' => true), +			), +		); +	} + +	/** +	 * @dataProvider data_upload_form +	 */ +	public function test_upload_form($upload, $expected, $plupload = array()) +	{ +		$this->request = $this->getMock('\phpbb\request\request'); +		$this->request->expects($this->any()) +			->method('file') +			->willReturn($upload); +		$filespec = new \phpbb\files\filespec( +			$this->filesystem, +			$this->language, +			$this->php_ini, +			new \FastImageSize\FastImageSize(), +			$this->phpbb_root_path, +			new \phpbb\mimetype\guesser(array( +				'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), +			))); +		$this->container->set('files.filespec', $filespec); +		$this->factory = new \phpbb\files\factory($this->container); +		$this->plupload = $this->getMockBuilder('\phpbb\plupload\plupload') +			->disableOriginalConstructor() +			->getMock(); +		$this->plupload->expects($this->any()) +			->method('handle_upload') +			->willReturn($plupload); + +		$type_form = new \phpbb\files\types\form($this->factory, $this->language, $this->php_ini, $this->plupload, $this->request); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('png')); +		$type_form->set_upload($upload); + + +		$file = $type_form->upload('foobar'); +		$this->assertSame($expected, $file->error); +		$this->assertInstanceOf('\phpbb\files\filespec', $file); +	} +} diff --git a/tests/files/types_local_test.php b/tests/files/types_local_test.php new file mode 100644 index 0000000000..f4fa7fad3f --- /dev/null +++ b/tests/files/types_local_test.php @@ -0,0 +1,163 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + +class phpbb_files_types_local_test extends phpbb_test_case +{ +	private $path; + +	private $filesystem; + +	/** @var \Symfony\Component\DependencyInjection\ContainerInterface */ +	protected $container; + +	/** @var \phpbb\files\factory */ +	protected $factory; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper */ +	protected $php_ini; + +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var \phpbb\request\request_interface */ +	protected $request; + +	/** @var \phpbb\plupload\plupload */ +	protected $plupload; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	protected function setUp() +	{ +		global $phpbb_root_path, $phpEx; + +		$this->request = $this->getMock('\phpbb\request\request'); +		$this->request->expects($this->any()) +			->method('file') +			->willReturn(array()); + +		$this->filesystem = new \phpbb\filesystem\filesystem(); +		$this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); +		$this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + +		$this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); +		$this->container->set('files.filespec', new \phpbb\files\filespec( +			$this->filesystem, +			$this->language, +			$this->php_ini, +			new \FastImageSize\FastImageSize(), +			$phpbb_root_path, +			new \phpbb\mimetype\guesser(array( +				'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), +			)))); +		$this->factory = new \phpbb\files\factory($this->container); +		$this->plupload = $this->getMockBuilder('\phpbb\plupload\plupload') +			->disableOriginalConstructor() +			->getMock(); +		$this->plupload->expects($this->any()) +			->method('handle_upload') +			->willReturn(array()); + +		$this->path = __DIR__ . '/fixture/'; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	public function test_upload_init_error() +	{ +		$filespec = $this->getMockBuilder('\phpbb\files\filespec') +			->disableOriginalConstructor() +			->getMock(); +		$filespec->expects($this->any()) +			->method('init_error') +			->willReturn(true); +		$filespec->expects($this->any()) +			->method('set_upload_ary') +			->willReturnSelf(); +		$filespec->expects($this->any()) +			->method('set_upload_namespace') +			->willReturnSelf(); +		$this->container->set('files.filespec', $filespec); +		$this->factory = new \phpbb\files\factory($this->container); + +		$type_local = new \phpbb\files\types\local($this->factory, $this->language, $this->php_ini, $this->request); + + +		$file = $type_local->upload('foo', false); +		$this->assertSame(array(''), $file->error); +		$this->assertInstanceOf('\phpbb\files\filespec', $file); +	} + +	public function data_upload_form() +	{ +		return array( +			array( +				'foo', +				array( +					'tmp_name'		=> 'foo', +					'size'			=> 500, +					'type'			=> 'image/png', +				), +				array('NOT_UPLOADED'), +			), +			array( +				'none', +				false, +				array('PHP_SIZE_OVERRUN'), +			), +			array( +				'tests/upload/fixture/png', +				array( +					'realname'			=> 'foo.png', +					'size'			=> 500, +					'type'			=> 'image/png', +					'local_mode'	=> true, +				), +				array(), +			), +		); +	} + +	/** +	 * @dataProvider data_upload_form +	 */ +	public function test_upload_form($filename, $upload_ary, $expected) +	{ +		$filespec = new \phpbb\files\filespec( +			$this->filesystem, +			$this->language, +			$this->php_ini, +			new \FastImageSize\FastImageSize(), +			$this->phpbb_root_path, +			new \phpbb\mimetype\guesser(array( +				'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), +			))); +		$filespec_local = new ReflectionProperty($filespec, 'local'); +		$filespec_local->setAccessible(true); +		$filespec_local->setValue($filespec, true); +		$this->container->set('files.filespec', $filespec); +		$this->factory = new \phpbb\files\factory($this->container); + +		$type_local = new \phpbb\files\types\local($this->factory, $this->language, $this->php_ini, $this->request); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('png')); +		$type_local->set_upload($upload); + + +		$file = $type_local->upload($filename, $upload_ary); +		$this->assertSame($expected, $file->error); +		$this->assertInstanceOf('\phpbb\files\filespec', $file); +	} +} diff --git a/tests/files/types_remote_test.php b/tests/files/types_remote_test.php new file mode 100644 index 0000000000..a85844ee78 --- /dev/null +++ b/tests/files/types_remote_test.php @@ -0,0 +1,141 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; +require_once dirname(__FILE__) . '/type_foo.php'; + +class phpbb_files_types_remote_test extends phpbb_test_case +{ +	private $path; + +	private $filesystem; + +	/** @var \Symfony\Component\DependencyInjection\ContainerInterface */ +	protected $container; + +	/** @var \phpbb\files\factory */ +	protected $factory; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper */ +	protected $php_ini; + +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var \phpbb\request\request_interface */ +	protected $request; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	protected function setUp() +	{ +		global $config, $phpbb_root_path, $phpEx; + +		$config = new \phpbb\config\config(array()); +		$this->request = $this->getMock('\phpbb\request\request'); + +		$this->filesystem = new \phpbb\filesystem\filesystem(); +		$this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); +		$this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + +		$this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); +		$this->container->set('files.filespec', new \phpbb\files\filespec( +			$this->filesystem, +			$this->language, +			$this->php_ini, +			new \FastImageSize\FastImageSize(), +			$phpbb_root_path, +			new \phpbb\mimetype\guesser(array( +				'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), +			)))); +		$this->factory = new \phpbb\files\factory($this->container); + +		$this->path = __DIR__ . '/fixture/'; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	public function test_upload_fsock_fail() +	{ +		$type_remote = new \phpbb\files\types\remote($this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('png')); +		$type_remote->set_upload($upload); + +		$file = $type_remote->upload('https://bärföö.com/foo.png'); + +		$this->assertSame(array('NOT_UPLOADED'), $file->error); +	} + +	public function data_get_max_file_size() +	{ +		return array( +			array('', 'http://example.com/foo/bar.png'), +			array('2k', 'http://example.com/foo/bar.png'), +			array('500k', 'http://example.com/foo/bar.png'), +			array('500M', 'http://example.com/foo/bar.png'), +			array('500m', 'http://example.com/foo/bar.png'), +			array('500k', 'http://google.com/.png', 'DISALLOWED_CONTENT'), +			array('1', 'http://google.com/.png', 'WRONG_FILESIZE'), +			array('500g', 'http://example.com/foo/bar.png'), +			array('foobar', 'http://example.com/foo/bar.png'), +			array('-5k', 'http://example.com/foo/bar.png'), +		); +	} + +	/** +	 * @dataProvider data_get_max_file_size +	 */ +	public function test_get_max_file_size($max_file_size, $link, $expected = 'URL_NOT_FOUND') +	{ +		$php_ini = $this->getMock('\bantu\IniGetWrapper\IniGetWrapper', array('getString')); +		$php_ini->expects($this->any()) +			->method('getString') +			->willReturn($max_file_size); +		$type_remote = new \phpbb\files\types\remote($this->factory, $this->language, $php_ini, $this->request, $this->phpbb_root_path); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('png')); +		$type_remote->set_upload($upload); + +		$file = $type_remote->upload($link); + +		$this->assertSame(array($expected), $file->error); +	} + +	public function test_upload_timeout() +	{ +		$type_remote = new \phpbb\files\types\remote($this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('png')); +		$type_remote->set_upload($upload); +		$upload->upload_timeout = -5; + +		$file = $type_remote->upload('http://google.com/.png'); + +		$this->assertSame(array('REMOTE_UPLOAD_TIMEOUT'), $file->error); +	} + +	public function test_upload_wrong_path() +	{ +		$type_remote = new \phpbb\files\types\foo($this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('png')); +		$type_remote->set_upload($upload); +		$type_remote::$tempnam_path = $this->phpbb_root_path . 'cache/wrong/path'; + +		$file = $type_remote->upload('http://google.com/.png'); + +		$this->assertSame(array('NOT_UPLOADED'), $file->error); +		$type_remote::$tempnam_path = ''; +	} +} diff --git a/tests/files/upload_test.php b/tests/files/upload_test.php new file mode 100644 index 0000000000..c41204a0d5 --- /dev/null +++ b/tests/files/upload_test.php @@ -0,0 +1,128 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_files_upload_test extends phpbb_test_case +{ +	private $path; + +	private $filesystem; + +	/** @var \Symfony\Component\DependencyInjection\ContainerInterface */ +	protected $container; + +	/** @var \phpbb\files\factory */ +	protected $factory; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper */ +	protected $php_ini; + +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var \phpbb\request\request_interface */ +	protected $request; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; + +	protected function setUp() +	{ +		// Global $config required by unique_id +		global $config, $phpbb_root_path, $phpEx; + +		if (!is_array($config)) +		{ +			$config = array(); +		} + +		$config['rand_seed'] = ''; +		$config['rand_seed_last_update'] = time() + 600; + +		$this->request = $this->getMock('\phpbb\request\request'); + +		$this->filesystem = new \phpbb\filesystem\filesystem(); +		$this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); +		$this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + +		$this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); +		$this->container->set('files.filespec', new \phpbb\files\filespec( +			$this->filesystem, +			$this->language, +			$this->php_ini, +			new \FastImageSize\FastImageSize(), +			$phpbb_root_path, +			new \phpbb\mimetype\guesser(array( +				'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), +			)))); +		$this->factory = new \phpbb\files\factory($this->container); + +		$this->path = __DIR__ . '/fixture/'; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	public function test_reset_vars() +	{ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_max_filesize(500); +		$this->assertEquals(500, $upload->max_filesize); +		$upload->reset_vars(); +		$this->assertEquals(0, $upload->max_filesize); +	} + +	public function test_set_disallowed_content() +	{ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$disallowed_content = new ReflectionProperty($upload, 'disallowed_content'); +		$disallowed_content->setAccessible(true); + +		$upload->set_disallowed_content(array('foo')); +		$this->assertEquals(array('foo'), $disallowed_content->getValue($upload)); +		$upload->set_disallowed_content(array('foo', 'bar', 'meh')); +		$this->assertEquals(array('foo', 'bar', 'meh'), $disallowed_content->getValue($upload)); +		$upload->set_disallowed_content(''); +		$this->assertEquals(array('foo', 'bar', 'meh'), $disallowed_content->getValue($upload)); +		$this->assertINstanceOf('\phpbb\files\upload', $upload->set_disallowed_content(array())); +		$this->assertEquals(array(), $disallowed_content->getValue($upload)); +		$upload->reset_vars(); +		$this->assertEquals(array(), $disallowed_content->getValue($upload)); +	} + +	public function test_is_valid() +	{ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$this->assertFalse($upload->is_valid('foobar')); +	} + +	public function data_internal_error() +	{ +		return array( +			array(UPLOAD_ERR_INI_SIZE, 'PHP_SIZE_OVERRUN'), +			array(UPLOAD_ERR_FORM_SIZE, 'WRONG_FILESIZE'), +			array(UPLOAD_ERR_PARTIAL, 'PARTIAL_UPLOAD'), +			array(UPLOAD_ERR_NO_FILE, 'NOT_UPLOADED'), +			array(UPLOAD_ERR_NO_TMP_DIR, 'NO_TEMP_DIR'), +			array(UPLOAD_ERR_CANT_WRITE, 'NO_TEMP_DIR'), +			array(UPLOAD_ERR_EXTENSION, 'PHP_UPLOAD_STOPPED'), +			array(9, false), +		); +	} + +	/** +	 * @dataProvider data_internal_error +	 */ +	public function test_assign_internal_error($error_code, $expected) +	{ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$this->assertSame($expected, $upload->assign_internal_error($error_code)); +	} +} diff --git a/tests/functional/fileupload_remote_test.php b/tests/functional/fileupload_remote_test.php index 4aa1a83b30..7e0f192b40 100644 --- a/tests/functional/fileupload_remote_test.php +++ b/tests/functional/fileupload_remote_test.php @@ -11,15 +11,29 @@  *  */ -require_once __DIR__ . '/../../phpBB/includes/functions_upload.php'; -  /**   * @group functional   */  class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case  { +	/** @var \phpbb\filesystem\filesystem_interface */  	protected $filesystem; +	/** @var \phpbb\files\factory */ +	protected $factory; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper */ +	protected $php_ini; + +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var \phpbb\request\request_interface */ +	protected $request; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; +  	public function setUp()  	{  		parent::setUp(); @@ -27,8 +41,7 @@ class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case  		// URL  		// Global $config required by unique_id -		// Global $user required by fileupload::remote_upload -		global $config, $user; +		global $config, $phpbb_root_path, $phpEx;  		if (!is_array($config))  		{ @@ -38,9 +51,17 @@ class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case  		$config['rand_seed'] = '';  		$config['rand_seed_last_update'] = time() + 600; -		$user = new phpbb_mock_user(); -		$user->lang = new phpbb_mock_lang();  		$this->filesystem = new \phpbb\filesystem\filesystem(); +		$this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); +		$this->request = $this->getMock('\phpbb\request\request'); +		$this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + +		$container = new phpbb_mock_container_builder(); +		$container->set('files.filespec', new \phpbb\files\filespec($this->filesystem, $this->language, $this->php_ini, new \FastImageSize\FastImageSize(), $this->phpbb_root_path)); +		$this->factory = new \phpbb\files\factory($container); +		$container->set('files.factory', $this->factory); +		$container->set('files.types.remote', new \phpbb\files\types\remote($this->factory, $this->language, $this->php_ini, $this->request, $phpbb_root_path)); +		$this->phpbb_root_path = $phpbb_root_path;  	}  	public function tearDown() @@ -52,30 +73,47 @@ class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case  	public function test_invalid_extension()  	{ -		$upload = new fileupload($this->filesystem, '', array('jpg'), 100); -		$file = $upload->remote_upload(self::$root_url . 'develop/blank.gif'); +		/** @var \phpbb\files\upload $upload */ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_error_prefix('') +			->set_allowed_extensions(array('jpg')) +			->set_max_filesize(100); +		$file = $upload->handle_upload('files.types.remote', self::$root_url . 'develop/blank.gif');  		$this->assertEquals('URL_INVALID', $file->error[0]);  	}  	public function test_empty_file()  	{ -		$upload = new fileupload($this->filesystem, '', array('jpg'), 100); -		$file = $upload->remote_upload(self::$root_url . 'develop/blank.jpg'); +		/** @var \phpbb\files\upload $upload */ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_error_prefix('') +			->set_allowed_extensions(array('jpg')) +			->set_max_filesize(100); +		$file = $upload->handle_upload('files.types.remote', self::$root_url . 'develop/blank.jpg');  		$this->assertEquals('EMPTY_REMOTE_DATA', $file->error[0]);  	}  	public function test_successful_upload()  	{ -		$upload = new fileupload($this->filesystem, '', array('gif'), 1000); -		$file = $upload->remote_upload(self::$root_url . 'styles/prosilver/theme/images/forum_read.gif'); +		/** @var \phpbb\files\upload $upload */ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_error_prefix('') +			->set_allowed_extensions(array('gif')) +			->set_max_filesize(1000); +		$file = $upload->handle_upload('files.types.remote', self::$root_url . 'styles/prosilver/theme/images/forum_read.gif');  		$this->assertEquals(0, sizeof($file->error)); -		$this->assertTrue(file_exists($file->filename)); +		$this->assertTrue(file_exists($file->get('filename'))); +		$this->assertTrue($file->is_uploaded());  	}  	public function test_too_large()  	{ -		$upload = new fileupload($this->filesystem, '', array('gif'), 100); -		$file = $upload->remote_upload(self::$root_url . 'styles/prosilver/theme/images/forum_read.gif'); +		/** @var \phpbb\files\upload $upload */ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_error_prefix('') +			->set_allowed_extensions(array('gif')) +			->set_max_filesize(100); +		$file = $upload->handle_upload('files.types.remote', self::$root_url . 'styles/prosilver/theme/images/forum_read.gif');  		$this->assertEquals(1, sizeof($file->error));  		$this->assertEquals('WRONG_FILESIZE', $file->error[0]);  	} diff --git a/tests/installer/installer_config_test.php b/tests/installer/installer_config_test.php index c7334ebd93..7de23aea36 100644 --- a/tests/installer/installer_config_test.php +++ b/tests/installer/installer_config_test.php @@ -24,11 +24,11 @@ class phpbb_installer_config_test extends phpbb_test_case  	{  		$phpbb_root_path = __DIR__ . './../../phpBB/';  		$filesystem = $this->getMock('\phpbb\filesystem\filesystem'); -		$php_ini = $this->getMockBuilder('\phpbb\php\ini') +		$php_ini = $this->getMockBuilder('\bantu\IniGetWrapper\IniGetWrapper')  			->getMock(); -		$php_ini->method('get_int') +		$php_ini->method('getInt')  			->willReturn(-1); -		$php_ini->method('get_bytes') +		$php_ini->method('getBytes')  			->willReturn(-1);  		$this->config = new config($filesystem, $php_ini, $phpbb_root_path); diff --git a/tests/installer/module_base_test.php b/tests/installer/module_base_test.php index fd92c9b674..9578010047 100644 --- a/tests/installer/module_base_test.php +++ b/tests/installer/module_base_test.php @@ -43,7 +43,7 @@ class module_base_test extends phpbb_test_case  		$this->module = new test_installer_module($module_collection, true, false);  		$iohandler = $this->getMock('\phpbb\install\helper\iohandler\iohandler_interface'); -		$config = new \phpbb\install\helper\config(new \phpbb\filesystem\filesystem(), new \phpbb\php\ini(), '', 'php'); +		$config = new \phpbb\install\helper\config(new \phpbb\filesystem\filesystem(), new \bantu\IniGetWrapper\IniGetWrapper(), '', 'php');  		$this->module->setup($config, $iohandler);  	} diff --git a/tests/mock/fileupload.php b/tests/mock/fileupload.php index 8cc4d77ea1..5a0afc6cd3 100644 --- a/tests/mock/fileupload.php +++ b/tests/mock/fileupload.php @@ -19,9 +19,10 @@ class phpbb_mock_fileupload  {  	public $max_filesize = 100;  	public $error_prefix = ''; +	public $valid_dimensions = true;  	public function valid_dimensions($filespec)  	{ -		return true; +		return $this->valid_dimensions;  	}  } diff --git a/tests/plupload/plupload_test.php b/tests/plupload/plupload_test.php index aa7793567d..3312f4d0a0 100644 --- a/tests/plupload/plupload_test.php +++ b/tests/plupload/plupload_test.php @@ -48,7 +48,7 @@ class phpbb_plupload_test extends phpbb_test_case  			$config,  			new phpbb_mock_request,  			new \phpbb\user($lang, '\phpbb\datetime'), -			new \phpbb\php\ini, +			new \bantu\IniGetWrapper\IniGetWrapper,  			new \phpbb\mimetype\guesser(array(new \phpbb\mimetype\extension_guesser))  		); diff --git a/tests/upload/filespec_test.php b/tests/upload/filespec_test.php index f953970f64..1351b46002 100644 --- a/tests/upload/filespec_test.php +++ b/tests/upload/filespec_test.php @@ -13,7 +13,6 @@  require_once __DIR__ . '/../../phpBB/includes/functions.php';  require_once __DIR__ . '/../../phpBB/includes/utf/utf_tools.php'; -require_once __DIR__ . '/../../phpBB/includes/functions_upload.php';  class phpbb_filespec_test extends phpbb_test_case  { @@ -26,12 +25,16 @@ class phpbb_filespec_test extends phpbb_test_case  	private $filesystem;  	public $path; +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; +  	protected function setUp()  	{  		// Global $config required by unique_id -		// Global $user required by filespec::additional_checks and -		// filespec::move_file -		global $config, $user, $phpbb_filesystem; +		global $config, $phpbb_root_path, $phpEx;  		if (!is_array($config))  		{ @@ -45,9 +48,6 @@ class phpbb_filespec_test extends phpbb_test_case  		// See: phpBB/install/schemas/schema_data.sql  		$config['mime_triggers'] = 'body|head|html|img|plaintext|a href|pre|script|table|title'; -		$user = new phpbb_mock_user(); -		$user->lang = new phpbb_mock_lang(); -  		$this->config = &$config;  		$this->path = __DIR__ . '/fixture/'; @@ -76,8 +76,17 @@ class phpbb_filespec_test extends phpbb_test_case  		$guessers[2]->set_priority(-2);  		$guessers[3]->set_priority(-2);  		$this->mimetype_guesser = new \phpbb\mimetype\guesser($guessers); +		$this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); -		$this->filesystem = $phpbb_filesystem = new \phpbb\filesystem\filesystem(); +		$this->filesystem = new \phpbb\filesystem\filesystem(); +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	private function set_reflection_property(&$class, $property_name, $value) +	{ +		$property = new ReflectionProperty($class, $property_name); +		$property->setAccessible(true); +		$property->setValue($class, $value);  	}  	private function get_filespec($override = array()) @@ -91,14 +100,13 @@ class phpbb_filespec_test extends phpbb_test_case  			'error' => '',  		); -		return new filespec(array_merge($upload_ary, $override), null, $this->filesystem, $this->mimetype_guesser); +		$filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, new \FastImageSize\FastImageSize(), $this->phpbb_root_path, $this->mimetype_guesser); +		return $filespec->set_upload_ary(array_merge($upload_ary, $override));  	}  	protected function tearDown()  	{ -		global $user;  		$this->config = array(); -		$user = null;  		$iterator = new DirectoryIterator($this->path . 'copies');  		foreach ($iterator as $fileinfo) @@ -111,6 +119,13 @@ class phpbb_filespec_test extends phpbb_test_case  		}  	} +	public function test_empty_upload_ary() +	{ +		$filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, new \FastImageSize\FastImageSize(), $this->phpbb_root_path, $this->mimetype_guesser); +		$this->assertInstanceOf('\phpbb\files\filespec', $filespec->set_upload_ary(array())); +		$this->assertTrue($filespec->init_error()); +	} +  	public function additional_checks_variables()  	{  		// False here just indicates the file is too large and fails the @@ -132,13 +147,26 @@ class phpbb_filespec_test extends phpbb_test_case  	{  		$upload = new phpbb_mock_fileupload();  		$filespec = $this->get_filespec(); -		$filespec->upload = $upload; -		$filespec->file_moved = true; -		$filespec->filesize = $filespec->get_filesize($this->path . $filename); +		$filespec->set_upload_namespace($upload); +		$this->set_reflection_property($filespec, 'file_moved', true); +		$this->set_reflection_property($filespec, 'filesize', $filespec->get_filesize($this->path . $filename));  		$this->assertEquals($expected, $filespec->additional_checks());  	} +	public function test_additional_checks_dimensions() +	{ +		$upload = new phpbb_mock_fileupload(); +		$filespec = $this->get_filespec(); +		$filespec->set_upload_namespace($upload); +		$upload->valid_dimensions = false; +		$this->set_reflection_property($filespec, 'file_moved', true); +		$upload->max_filesize = 0; + +		$this->assertEquals(false, $filespec->additional_checks()); +		$this->assertSame(array('WRONG_SIZE'), $filespec->error); +	} +  	public function check_content_variables()  	{  		// False here indicates that a file is non-binary and contains @@ -173,6 +201,7 @@ class phpbb_filespec_test extends phpbb_test_case  			array($chunks[2] . $chunks[9]),  			array($chunks[3] . $chunks[4]),  			array($chunks[5] . $chunks[6]), +			array('foobar.png'),  		);  	} @@ -184,7 +213,7 @@ class phpbb_filespec_test extends phpbb_test_case  		$bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|');  		$filespec = $this->get_filespec(array('name' => $filename));  		$filespec->clean_filename('real', self::PREFIX); -		$name = $filespec->realname; +		$name = $filespec->get('realname');  		$this->assertEquals(0, preg_match('/%(\w{2})/', $name));  		foreach ($bad_chars as $char) @@ -200,7 +229,7 @@ class phpbb_filespec_test extends phpbb_test_case  		{  			$filespec = $this->get_filespec();  			$filespec->clean_filename('unique', self::PREFIX); -			$name = $filespec->realname; +			$name = $filespec->get('realname');  			$this->assertEquals(strlen($name), 32 + strlen(self::PREFIX));  			$this->assertRegExp('#^[A-Za-z0-9]+$#', substr($name, strlen(self::PREFIX))); @@ -209,6 +238,55 @@ class phpbb_filespec_test extends phpbb_test_case  		}  	} +	public function test_clean_filename_unique_ext() +	{ +		$filenames = array(); +		for ($tests = 0; $tests < self::TEST_COUNT; $tests++) +		{ +			$filespec = $this->get_filespec(array('name' => 'foobar.jpg')); +			$filespec->clean_filename('unique_ext', self::PREFIX); +			$name = $filespec->get('realname'); + +			$this->assertEquals(strlen($name), 32 + strlen(self::PREFIX) + strlen('.jpg')); +			$this->assertRegExp('#^[A-Za-z0-9]+\.jpg$#', substr($name, strlen(self::PREFIX))); +			$this->assertFalse(isset($filenames[$name])); +			$filenames[$name] = true; +		} +	} + +	public function data_clean_filename_avatar() +	{ +		return array( +			array(false, false, ''), +			array('foobar.png', 'u5.png', 'avatar', 'u', 5), +			array('foobar.png', 'g9.png', 'avatar', 'g', 9), + +		); +	} + +	/** +	 * @dataProvider data_clean_filename_avatar +	 */ +	public function test_clean_filename_avatar($filename, $expected, $mode, $prefix = '', $user_id = '') +	{ +		$filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, new \FastImageSize\FastImageSize(), $this->phpbb_root_path, $this->mimetype_guesser); + +		if ($filename) +		{ +			$upload_ary = array( +				'name' => $filename, +				'type' => '', +				'size' => '', +				'tmp_name' => '', +				'error' => '', +			); +			$filespec->set_upload_ary($upload_ary); +		} +		$filespec->clean_filename($mode, $prefix, $user_id); + +		$this->assertSame($expected, $filespec->get('realname')); +	} +  	public function get_extension_variables()  	{  		return array( @@ -226,7 +304,7 @@ class phpbb_filespec_test extends phpbb_test_case  	 */  	public function test_get_extension($filename, $expected)  	{ -		$this->assertEquals($expected, filespec::get_extension($filename)); +		$this->assertEquals($expected, \phpbb\files\filespec::get_extension($filename));  	}  	public function is_image_variables() @@ -289,7 +367,7 @@ class phpbb_filespec_test extends phpbb_test_case  			array('txt_copy', 'txt_as_img', 'image/jpg', 'txt', false, true),  			array('txt_copy_2', 'txt_moved', 'text/plain', 'txt', false, true),  			array('jpg_copy', 'jpg_moved', 'image/png', 'jpg', false, true), -			array('png_copy', 'png_moved', 'image/png', 'jpg', 'IMAGE_FILETYPE_MISMATCH png jpg', true), +			array('png_copy', 'png_moved', 'image/png', 'jpg', 'Image file type mismatch: expected extension png but extension jpg given.', true),  		);  	} @@ -300,8 +378,7 @@ class phpbb_filespec_test extends phpbb_test_case  	{  		// Global $phpbb_root_path and $phpEx are required by phpbb_chmod  		global $phpbb_root_path, $phpEx; -		$phpbb_root_path = ''; -		$phpEx = 'php'; +		$this->phpbb_root_path = '';  		$upload = new phpbb_mock_fileupload();  		$upload->max_filesize = self::UPLOAD_MAX_FILESIZE; @@ -311,18 +388,137 @@ class phpbb_filespec_test extends phpbb_test_case  			'name' => $realname,  			'type' => $mime_type,  		)); -		$filespec->extension = $extension; -		$filespec->upload = $upload; -		$filespec->local = true; +		$this->set_reflection_property($filespec, 'extension', $extension); +		$filespec->set_upload_namespace($upload); +		$this->set_reflection_property($filespec, 'local', true);  		$this->assertEquals($expected, $filespec->move_file($this->path . 'copies')); -		$this->assertEquals($filespec->file_moved, file_exists($this->path . 'copies/' . $realname)); +		$this->assertEquals($filespec->get('file_moved'), file_exists($this->path . 'copies/' . $realname));  		if ($error)  		{  			$this->assertEquals($error, $filespec->error[0]);  		} -		$phpEx = ''; +		$this->phpbb_root_path = $phpbb_root_path; +	} + +	public function test_move_file_error() +	{ +		$filespec = $this->get_filespec(); +		$this->assertFalse($filespec->move_file('foobar')); +		$filespec->error[] = 'foo'; +		$this->assertFalse($filespec->move_file('foo')); +	} + +	public function data_move_file_copy() +	{ +		return array( +			array('gif_copy', true, false, array()), +			array('gif_copy', true, true, array()), +			array('foo_bar', false, false, array('GENERAL_UPLOAD_ERROR')), +			array('foo_bar', false, true, array('GENERAL_UPLOAD_ERROR')), +		); +	} + +	/** +	 * @dataProvider data_move_file_copy +	 */ +	public function test_move_file_copy($tmp_name, $move_success, $safe_mode_on, $expected_error) +	{ +		// Initialise a blank filespec object for use with trivial methods +		$upload_ary = array( +			'name' => 'gif_moved', +			'type' => 'image/gif', +			'size' => '', +			'tmp_name' => $this->path . 'copies/' . $tmp_name, +			'error' => '', +		); + +		$php_ini = $this->getMockBuilder('\bantu\IniGetWrapper\IniGetWrapper') +			->getMock(); +		$php_ini->expects($this->any()) +			->method('getBool') +			->with($this->anything()) +			->willReturn($safe_mode_on); +		$upload = new phpbb_mock_fileupload(); +		$upload->max_filesize = self::UPLOAD_MAX_FILESIZE; +		$filespec = new \phpbb\files\filespec($this->filesystem, $this->language, $php_ini, new \FastImageSize\FastImagesize,  '', $this->mimetype_guesser); +		$filespec->set_upload_ary($upload_ary); +		$this->set_reflection_property($filespec, 'local', false); +		$this->set_reflection_property($filespec, 'extension', 'gif'); +		$filespec->set_upload_namespace($upload); + +		$this->assertEquals($move_success, $filespec->move_file($this->path . 'copies')); +		$this->assertEquals($filespec->get('file_moved'), file_exists($this->path . 'copies/gif_moved')); +		$this->assertSame($expected_error, $filespec->error); +	} + +	public function data_move_file_imagesize() +	{ +		return array( +			array( +				array( +					'width'		=> 2, +					'height'	=> 2, +					'type'		=> IMAGETYPE_GIF, +				), +				array() +			), +			array( +				array( +					'width'		=> 2, +					'height'	=> 2, +					'type'		=> -1, +				), +				array('Image file type -1 for mimetype image/gif not supported.') +			), +			array( +				array( +					'width'		=> 0, +					'height'	=> 0, +					'type'		=> IMAGETYPE_GIF, +				), +				array('The image file you tried to attach is invalid.') +			), +			array( +				false, +				array('It was not possible to determine the dimensions of the image. Please verify that the URL you entered is correct.') +			) +		); +	} + +	/** +	 * @dataProvider data_move_file_imagesize +	 */ +	public function test_move_file_imagesize($imagesize_return, $expected_error) +	{ +		// Initialise a blank filespec object for use with trivial methods +		$upload_ary = array( +			'name' => 'gif_moved', +			'type' => 'image/gif', +			'size' => '', +			'tmp_name' => $this->path . 'copies/gif_copy', +			'error' => '', +		); + +		$imagesize = $this->getMockBuilder('\FastImageSize\FastImageSize') +			->getMock(); +		$imagesize->expects($this->any()) +			->method('getImageSize') +			->with($this->anything()) +			->willReturn($imagesize_return); + +		$upload = new phpbb_mock_fileupload(); +		$upload->max_filesize = self::UPLOAD_MAX_FILESIZE; +		$filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, $imagesize,  '', $this->mimetype_guesser); +		$filespec->set_upload_ary($upload_ary); +		$this->set_reflection_property($filespec, 'local', false); +		$this->set_reflection_property($filespec, 'extension', 'gif'); +		$filespec->set_upload_namespace($upload); + +		$this->assertEquals(true, $filespec->move_file($this->path . 'copies')); +		$this->assertEquals($filespec->get('file_moved'), file_exists($this->path . 'copies/gif_moved')); +		$this->assertSame($expected_error, $filespec->error);  	}  	/** @@ -336,6 +532,29 @@ class phpbb_filespec_test extends phpbb_test_case  		$type_cast_helper->set_var($upload_name, $filename, 'string', true, true);  		$filespec = $this->get_filespec(array('name'=> $upload_name)); -		$this->assertSame(trim(utf8_basename(htmlspecialchars($filename))), $filespec->uploadname); +		$this->assertSame(trim(utf8_basename(htmlspecialchars($filename))), $filespec->get('uploadname')); +	} + +	public function test_is_uploaded() +	{ +		$filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, new \FastImageSize\FastImageSize(), $this->phpbb_root_path, null); +		$reflection_filespec = new ReflectionClass($filespec); +		$plupload_property = $reflection_filespec->getProperty('plupload'); +		$plupload_property->setAccessible(true); +		$plupload_mock = $this->getMockBuilder('\phpbb\plupload\plupload') +			->disableOriginalConstructor() +			->getMock(); +		$plupload_mock->expects($this->any()) +			->method('is_active') +			->will($this->returnValue(true)); +		$plupload_property->setValue($filespec, $plupload_mock); +		$is_uploaded = $reflection_filespec->getMethod('is_uploaded'); + +		// Plupload is active and file does not exist +		$this->assertFalse($is_uploaded->invoke($filespec)); + +		// Plupload is not active and file was not uploaded +		$plupload_property->setValue($filespec, null); +		$this->assertFalse($is_uploaded->invoke($filespec));  	}  } diff --git a/tests/upload/fileupload_test.php b/tests/upload/fileupload_test.php index 9de384b64f..05cb8dcf93 100644 --- a/tests/upload/fileupload_test.php +++ b/tests/upload/fileupload_test.php @@ -13,7 +13,6 @@  require_once __DIR__ . '/../../phpBB/includes/functions.php';  require_once __DIR__ . '/../../phpBB/includes/utf/utf_tools.php'; -require_once __DIR__ . '/../../phpBB/includes/functions_upload.php';  require_once __DIR__ . '/../mock/filespec.php';  class phpbb_fileupload_test extends phpbb_test_case @@ -22,29 +21,80 @@ class phpbb_fileupload_test extends phpbb_test_case  	private $filesystem; +	/** @var \Symfony\Component\DependencyInjection\ContainerInterface */ +	protected $container; + +	/** @var \phpbb\files\factory */ +	protected $factory; + +	/** @var \bantu\IniGetWrapper\IniGetWrapper */ +	protected $php_ini; + +	/** @var \phpbb\language\language */ +	protected $language; + +	/** @var \phpbb\request\request_interface */ +	protected $request; + +	/** @var string phpBB root path */ +	protected $phpbb_root_path; +  	protected function setUp()  	{  		// Global $config required by unique_id -		// Global $user required by several functions dealing with translations -		// Global $request required by form_upload, local_upload and is_valid -		global $config, $user, $request, $phpbb_filesystem; +		global $config, $phpbb_root_path, $phpEx;  		if (!is_array($config))  		{ -			$config = array(); +			$config = new \phpbb\config\config(array());  		}  		$config['rand_seed'] = '';  		$config['rand_seed_last_update'] = time() + 600; -		$user = new phpbb_mock_user(); -		$user->lang = new phpbb_mock_lang(); - -		$request = new phpbb_mock_request(); - -		$this->filesystem = $phpbb_filesystem = new \phpbb\filesystem\filesystem(); +		$this->request = $this->getMock('\phpbb\request\request'); +		$this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + +		$this->filesystem = new \phpbb\filesystem\filesystem(); +		$this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); +		$guessers = array( +			new \Symfony\Component\HttpFoundation\File\MimeType\FileinfoMimeTypeGuesser(), +			new \Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser(), +			new \phpbb\mimetype\content_guesser(), +			new \phpbb\mimetype\extension_guesser(), +		); +		$guessers[2]->set_priority(-2); +		$guessers[3]->set_priority(-2); +		$this->mimetype_guesser = new \phpbb\mimetype\guesser($guessers); + +		$this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); +		$this->container->set('files.filespec', new \phpbb\files\filespec( +			$this->filesystem, +			$this->language, +			$this->php_ini, +			new \FastImageSize\FastImageSize(), +			$phpbb_root_path, +			new \phpbb\mimetype\guesser(array( +				'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), +			)))); +		$this->factory = new \phpbb\files\factory($this->container); +		$plupload = new \phpbb\plupload\plupload($phpbb_root_path, $config, $this->request, new \phpbb\user($this->language, '\phpbb\datetime'), $this->php_ini, $this->mimetype_guesser); +		$this->container->set('files.types.form', new \phpbb\files\types\form( +			$this->factory, +			$this->language, +			$this->php_ini, +			$plupload, +			$this->request +		), phpbb_mock_container_builder::SCOPE_PROTOTYPE); +		$this->container->set('files.types.local', new \phpbb\files\types\local( +			$this->factory, +			$this->language, +			$this->php_ini, +			$this->request +		), phpbb_mock_container_builder::SCOPE_PROTOTYPE);  		$this->path = __DIR__ . '/fixture/'; +		$this->phpbb_root_path = $phpbb_root_path;  	}  	private function gen_valid_filespec() @@ -69,15 +119,38 @@ class phpbb_fileupload_test extends phpbb_test_case  	public function test_common_checks_invalid_extension()  	{ -		$upload = new fileupload($this->filesystem, '', array('png'), 100); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('png')) +			->set_max_filesize(100);  		$file = $this->gen_valid_filespec();  		$upload->common_checks($file);  		$this->assertEquals('DISALLOWED_EXTENSION', $file->error[0]);  	} +	public function test_common_checks_disallowed_content() +	{ +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('jpg')) +			->set_max_filesize(1000); +		$file = new \phpbb\files\filespec($this->filesystem, $this->language, $this->php_ini, new \FastImageSize\FastImageSize(), $this->phpbb_root_path); +		$file->set_upload_ary(array( +				'size'	=> 50, +				'tmp_name'	=> dirname(__FILE__) . '/fixture/disallowed', +				'name'		=> 'disallowed.jpg', +				'type'		=> 'image/jpg' +			)) +			->set_upload_namespace($upload); +		file_put_contents(dirname(__FILE__) . '/fixture/disallowed', '<body>' . file_get_contents(dirname(__FILE__) . '/fixture/jpg')); +		$upload->common_checks($file); +		$this->assertEquals('DISALLOWED_CONTENT', $file->error[0]); +		unlink(dirname(__FILE__) . '/fixture/disallowed'); +	} +  	public function test_common_checks_invalid_filename()  	{ -		$upload = new fileupload($this->filesystem, '', array('jpg'), 100); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('jpg')) +			->set_max_filesize(100);  		$file = $this->gen_valid_filespec();  		$file->realname = 'invalid?';  		$upload->common_checks($file); @@ -86,7 +159,9 @@ class phpbb_fileupload_test extends phpbb_test_case  	public function test_common_checks_too_large()  	{ -		$upload = new fileupload($this->filesystem, '', array('jpg'), 100); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('jpg')) +			->set_max_filesize(100);  		$file = $this->gen_valid_filespec();  		$file->filesize = 1000;  		$upload->common_checks($file); @@ -95,7 +170,9 @@ class phpbb_fileupload_test extends phpbb_test_case  	public function test_common_checks_valid_file()  	{ -		$upload = new fileupload($this->filesystem, '', array('jpg'), 1000); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('jpg')) +			->set_max_filesize(1000);  		$file = $this->gen_valid_filespec();  		$upload->common_checks($file);  		$this->assertEquals(0, sizeof($file->error)); @@ -103,33 +180,41 @@ class phpbb_fileupload_test extends phpbb_test_case  	public function test_local_upload()  	{ -		$upload = new fileupload($this->filesystem, '', array('jpg'), 1000); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('jpg')) +			->set_max_filesize(1000);  		copy($this->path . 'jpg', $this->path . 'jpg.jpg'); -		$file = $upload->local_upload($this->path . 'jpg.jpg'); +		$file = $upload->handle_upload('files.types.local', $this->path . 'jpg.jpg');  		$this->assertEquals(0, sizeof($file->error)); -		unlink($this->path . 'jpg.jpg'); +		$this->assertFalse($file->additional_checks()); +		$this->assertTrue($file->move_file('../tests/upload/fixture/copies', true)); +		$file->remove();  	}  	public function test_move_existent_file()  	{ -		$upload = new fileupload($this->filesystem, '', array('jpg'), 1000); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('jpg')) +			->set_max_filesize(1000);  		copy($this->path . 'jpg', $this->path . 'jpg.jpg'); -		$file = $upload->local_upload($this->path . 'jpg.jpg'); +		$file = $upload->handle_upload('files.types.local', $this->path . 'jpg.jpg');  		$this->assertEquals(0, sizeof($file->error));  		$this->assertFalse($file->move_file('../tests/upload/fixture')); -		$this->assertFalse($file->file_moved); +		$this->assertFalse($file->get('file_moved'));  		$this->assertEquals(1, sizeof($file->error));  	}  	public function test_move_existent_file_overwrite()  	{ -		$upload = new fileupload($this->filesystem, '', array('jpg'), 1000); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(array('jpg')) +			->set_max_filesize(1000);  		copy($this->path . 'jpg', $this->path . 'jpg.jpg');  		copy($this->path . 'jpg', $this->path . 'copies/jpg.jpg'); -		$file = $upload->local_upload($this->path . 'jpg.jpg'); +		$file = $upload->handle_upload('files.types.local', $this->path . 'jpg.jpg');  		$this->assertEquals(0, sizeof($file->error));  		$file->move_file('../tests/upload/fixture/copies', true);  		$this->assertEquals(0, sizeof($file->error)); @@ -138,7 +223,10 @@ class phpbb_fileupload_test extends phpbb_test_case  	public function test_valid_dimensions()  	{ -		$upload = new fileupload($this->filesystem, '', false, false, 1, 1, 100, 100); +		$upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); +		$upload->set_allowed_extensions(false) +			->set_max_filesize(false) +			->set_allowed_dimensions(1, 1, 100, 100);  		$file1 = $this->gen_valid_filespec();  		$file2 = $this->gen_valid_filespec(); diff --git a/tests/wrapper/phpbb_php_ini_fake.php b/tests/wrapper/phpbb_php_ini_fake.php index f218ff6d03..300ce30cfe 100644 --- a/tests/wrapper/phpbb_php_ini_fake.php +++ b/tests/wrapper/phpbb_php_ini_fake.php @@ -11,7 +11,7 @@  *  */ -class phpbb_php_ini_fake extends \phpbb\php\ini +class phpbb_php_ini_fake extends \bantu\IniGetWrapper\IniGetWrapper  {  	function get($varname)  	{ diff --git a/tests/wrapper/phpbb_php_ini_test.php b/tests/wrapper/phpbb_php_ini_test.php index 27c2487f0a..9f46a78d5b 100644 --- a/tests/wrapper/phpbb_php_ini_test.php +++ b/tests/wrapper/phpbb_php_ini_test.php @@ -16,6 +16,7 @@ require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';  class phpbb_wrapper_phpbb_php_ini_test extends phpbb_test_case  { +	/** @var \phpbb_php_ini_fake php_ini */  	protected $php_ini;  	public function setUp() @@ -25,44 +26,44 @@ class phpbb_wrapper_phpbb_php_ini_test extends phpbb_test_case  	public function test_get_string()  	{ -		$this->assertSame(false, $this->php_ini->get_string(false)); -		$this->assertSame('phpbb', $this->php_ini->get_string(' phpbb ')); +		$this->assertSame('', $this->php_ini->getString(false)); +		$this->assertSame('phpbb', $this->php_ini->getString(' phpbb '));  	}  	public function test_get_bool()  	{ -		$this->assertSame(true, $this->php_ini->get_bool('ON')); -		$this->assertSame(true, $this->php_ini->get_bool('on')); -		$this->assertSame(true, $this->php_ini->get_bool('1')); +		$this->assertSame(true, $this->php_ini->getBool('ON')); +		$this->assertSame(true, $this->php_ini->getBool('on')); +		$this->assertSame(true, $this->php_ini->getBool('1')); -		$this->assertSame(false, $this->php_ini->get_bool('OFF')); -		$this->assertSame(false, $this->php_ini->get_bool('off')); -		$this->assertSame(false, $this->php_ini->get_bool('0')); -		$this->assertSame(false, $this->php_ini->get_bool('')); +		$this->assertSame(false, $this->php_ini->getBool('OFF')); +		$this->assertSame(false, $this->php_ini->getBool('off')); +		$this->assertSame(false, $this->php_ini->getBool('0')); +		$this->assertSame(false, $this->php_ini->getBool(''));  	}  	public function test_get_int()  	{ -		$this->assertSame(1234, $this->php_ini->get_int('1234')); -		$this->assertSame(-12345, $this->php_ini->get_int('-12345')); -		$this->assertSame(false, $this->php_ini->get_int('phpBB')); +		$this->assertSame(1234, $this->php_ini->getNumeric('1234')); +		$this->assertSame(-12345, $this->php_ini->getNumeric('-12345')); +		$this->assertSame(null, $this->php_ini->getNumeric('phpBB'));  	}  	public function test_get_float()  	{ -		$this->assertSame(1234.0, $this->php_ini->get_float('1234')); -		$this->assertSame(-12345.0, $this->php_ini->get_float('-12345')); -		$this->assertSame(false, $this->php_ini->get_float('phpBB')); +		$this->assertSame(1234.0, $this->php_ini->getNumeric('1234.0')); +		$this->assertSame(-12345.0, $this->php_ini->getNumeric('-12345.0')); +		$this->assertSame(null, $this->php_ini->getNumeric('phpBB'));  	}  	public function test_get_bytes_invalid()  	{ -		$this->assertSame(false, $this->php_ini->get_bytes(false)); -		$this->assertSame(false, $this->php_ini->get_bytes('phpBB')); -		$this->assertSame(false, $this->php_ini->get_bytes('k')); -		$this->assertSame(false, $this->php_ini->get_bytes('-k')); -		$this->assertSame(false, $this->php_ini->get_bytes('M')); -		$this->assertSame(false, $this->php_ini->get_bytes('-M')); +		$this->assertSame(null, $this->php_ini->getBytes(false)); +		$this->assertSame(null, $this->php_ini->getBytes('phpBB')); +		$this->assertSame(null, $this->php_ini->getBytes('k')); +		$this->assertSame(null, $this->php_ini->getBytes('-k')); +		$this->assertSame(null, $this->php_ini->getBytes('M')); +		$this->assertSame(null, $this->php_ini->getBytes('-M'));  	}  	/** @@ -70,7 +71,7 @@ class phpbb_wrapper_phpbb_php_ini_test extends phpbb_test_case  	*/  	public function test_get_bytes($expected, $value)  	{ -		$actual = $this->php_ini->get_bytes($value); +		$actual = $this->php_ini->getBytes($value);  		$this->assertTrue(is_float($actual) || is_int($actual));  		$this->assertEquals($expected, $actual);  | 
