mirror of
				https://github.com/phpbb/phpbb.git
				synced 2025-10-26 13:16:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			403 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?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\plupload;
 | |
| 
 | |
| /**
 | |
| * This class handles all server-side plupload functions
 | |
| */
 | |
| class plupload
 | |
| {
 | |
| 	/**
 | |
| 	* @var string
 | |
| 	*/
 | |
| 	protected $phpbb_root_path;
 | |
| 
 | |
| 	/**
 | |
| 	* @var \phpbb\config\config
 | |
| 	*/
 | |
| 	protected $config;
 | |
| 
 | |
| 	/**
 | |
| 	* @var \phpbb\request\request_interface
 | |
| 	*/
 | |
| 	protected $request;
 | |
| 
 | |
| 	/**
 | |
| 	* @var \phpbb\user
 | |
| 	*/
 | |
| 	protected $user;
 | |
| 
 | |
| 	/**
 | |
| 	* @var \phpbb\php\ini
 | |
| 	*/
 | |
| 	protected $php_ini;
 | |
| 
 | |
| 	/**
 | |
| 	* @var \phpbb\mimetype\guesser
 | |
| 	*/
 | |
| 	protected $mimetype_guesser;
 | |
| 
 | |
| 	/**
 | |
| 	* Final destination for uploaded files, i.e. the "files" directory.
 | |
| 	* @var string
 | |
| 	*/
 | |
| 	protected $upload_directory;
 | |
| 
 | |
| 	/**
 | |
| 	* Temporary upload directory for plupload uploads.
 | |
| 	* @var string
 | |
| 	*/
 | |
| 	protected $temporary_directory;
 | |
| 
 | |
| 	/**
 | |
| 	* Constructor.
 | |
| 	*
 | |
| 	* @param string $phpbb_root_path
 | |
| 	* @param \phpbb\config\config $config
 | |
| 	* @param \phpbb\request\request_interface $request
 | |
| 	* @param \phpbb\user $user
 | |
| 	* @param \phpbb\php\ini $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)
 | |
| 	{
 | |
| 		$this->phpbb_root_path = $phpbb_root_path;
 | |
| 		$this->config = $config;
 | |
| 		$this->request = $request;
 | |
| 		$this->user = $user;
 | |
| 		$this->php_ini = $php_ini;
 | |
| 		$this->mimetype_guesser = $mimetype_guesser;
 | |
| 
 | |
| 		$this->set_default_directories();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Plupload allows for chunking so we must check for that and assemble
 | |
| 	* the whole file first before performing any checks on it.
 | |
| 	*
 | |
| 	* @param string $form_name The name of the file element in the upload form
 | |
| 	*
 | |
| 	* @return array|null	null if there are no chunks to piece together
 | |
| 	*						otherwise array containing the path to the
 | |
| 	*						pieced-together file and its size
 | |
| 	*/
 | |
| 	public function handle_upload($form_name)
 | |
| 	{
 | |
| 		$chunks_expected = $this->request->variable('chunks', 0);
 | |
| 
 | |
| 		// If chunking is disabled or we are not using plupload, just return
 | |
| 		// and handle the file as usual
 | |
| 		if ($chunks_expected < 2)
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$file_name = $this->request->variable('name', '');
 | |
| 		$chunk = $this->request->variable('chunk', 0);
 | |
| 
 | |
| 		$this->user->add_lang('plupload');
 | |
| 		$this->prepare_temporary_directory();
 | |
| 
 | |
| 		$file_path = $this->temporary_filepath($file_name);
 | |
| 		$this->integrate_uploaded_file($form_name, $chunk, $file_path);
 | |
| 
 | |
| 		// If we are done with all the chunks, strip the .part suffix and then
 | |
| 		// handle the resulting file as normal, otherwise die and await the
 | |
| 		// next chunk.
 | |
| 		if ($chunk == $chunks_expected - 1)
 | |
| 		{
 | |
| 			rename("{$file_path}.part", $file_path);
 | |
| 
 | |
| 			// Reset upload directories to defaults once completed
 | |
| 			$this->set_default_directories();
 | |
| 
 | |
| 			// Need to modify some of the $_FILES values to reflect the new file
 | |
| 			return array(
 | |
| 				'tmp_name' => $file_path,
 | |
| 				'name' => $this->request->variable('real_filename', ''),
 | |
| 				'size' => filesize($file_path),
 | |
| 				'type' => $this->mimetype_guesser->guess($file_path, $file_name),
 | |
| 			);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			$json_response = new \phpbb\json_response();
 | |
| 			$json_response->send(array(
 | |
| 				'jsonrpc' => '2.0',
 | |
| 				'id' => 'id',
 | |
| 				'result' => null,
 | |
| 			));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Fill in the plupload configuration options in the template
 | |
| 	*
 | |
| 	* @param \phpbb\cache\service		$cache
 | |
| 	* @param \phpbb\template\template	$template
 | |
| 	* @param string						$s_action The URL to submit the POST data to
 | |
| 	* @param int						$forum_id The ID of the forum
 | |
| 	* @param int						$max_files Maximum number of files allowed. 0 for unlimited.
 | |
| 	*
 | |
| 	* @return null
 | |
| 	*/
 | |
| 	public function configure(\phpbb\cache\service $cache, \phpbb\template\template $template, $s_action, $forum_id, $max_files)
 | |
| 	{
 | |
| 		$filters = $this->generate_filter_string($cache, $forum_id);
 | |
| 		$chunk_size = $this->get_chunk_size();
 | |
| 		$resize = $this->generate_resize_string();
 | |
| 
 | |
| 		$template->assign_vars(array(
 | |
| 			'S_RESIZE'			=> $resize,
 | |
| 			'S_PLUPLOAD'		=> true,
 | |
| 			'FILTERS'			=> $filters,
 | |
| 			'CHUNK_SIZE'		=> $chunk_size,
 | |
| 			'S_PLUPLOAD_URL'	=> htmlspecialchars_decode($s_action),
 | |
| 			'MAX_ATTACHMENTS'	=> $max_files,
 | |
| 			'ATTACH_ORDER'		=> ($this->config['display_order']) ? 'asc' : 'desc',
 | |
| 			'L_TOO_MANY_ATTACHMENTS'	=> $this->user->lang('TOO_MANY_ATTACHMENTS', $max_files),
 | |
| 		));
 | |
| 
 | |
| 		$this->user->add_lang('plupload');
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Checks whether the page request was sent by plupload or not
 | |
| 	*
 | |
| 	* @return bool
 | |
| 	*/
 | |
| 	public function is_active()
 | |
| 	{
 | |
| 		return $this->request->header('X-PHPBB-USING-PLUPLOAD', false);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Returns whether the current HTTP request is a multipart request.
 | |
| 	*
 | |
| 	* @return bool
 | |
| 	*/
 | |
| 	public function is_multipart()
 | |
| 	{
 | |
| 		$content_type = $this->request->server('CONTENT_TYPE');
 | |
| 
 | |
| 		return strpos($content_type, 'multipart') === 0;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Sends an error message back to the client via JSON response
 | |
| 	*
 | |
| 	* @param int $code		The error code
 | |
| 	* @param string $msg	The translation string of the message to be sent
 | |
| 	*
 | |
| 	* @return null
 | |
| 	*/
 | |
| 	public function emit_error($code, $msg)
 | |
| 	{
 | |
| 		$json_response = new \phpbb\json_response();
 | |
| 		$json_response->send(array(
 | |
| 			'jsonrpc' => '2.0',
 | |
| 			'id' => 'id',
 | |
| 			'error' => array(
 | |
| 				'code' => $code,
 | |
| 				'message' => $this->user->lang($msg),
 | |
| 			),
 | |
| 		));
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Looks at the list of allowed extensions and generates a string
 | |
| 	* appropriate for use in configuring plupload with
 | |
| 	*
 | |
| 	* @param \phpbb\cache\service $cache
 | |
| 	* @param string $forum_id The ID of the forum
 | |
| 	*
 | |
| 	* @return string
 | |
| 	*/
 | |
| 	public function generate_filter_string(\phpbb\cache\service $cache, $forum_id)
 | |
| 	{
 | |
| 		$attach_extensions = $cache->obtain_attach_extensions($forum_id);
 | |
| 		unset($attach_extensions['_allowed_']);
 | |
| 		$groups = array();
 | |
| 
 | |
| 		// Re-arrange the extension array to $groups[$group_name][]
 | |
| 		foreach ($attach_extensions as $extension => $extension_info)
 | |
| 		{
 | |
| 			if (!isset($groups[$extension_info['group_name']]))
 | |
| 			{
 | |
| 				$groups[$extension_info['group_name']] = array();
 | |
| 			}
 | |
| 
 | |
| 			$groups[$extension_info['group_name']][] = $extension;
 | |
| 		}
 | |
| 
 | |
| 		$filters = array();
 | |
| 		foreach ($groups as $group => $extensions)
 | |
| 		{
 | |
| 			$filters[] = sprintf(
 | |
| 				"{title: '%s', extensions: '%s'}",
 | |
| 				addslashes(ucfirst(strtolower($group))),
 | |
| 				addslashes(implode(',', $extensions))
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		return implode(',', $filters);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Generates a string that is used to tell plupload to automatically resize
 | |
| 	* files before uploading them.
 | |
| 	*
 | |
| 	* @return string
 | |
| 	*/
 | |
| 	public function generate_resize_string()
 | |
| 	{
 | |
| 		$resize = '';
 | |
| 		if ($this->config['img_max_height'] > 0 && $this->config['img_max_width'] > 0)
 | |
| 		{
 | |
| 			$resize = sprintf(
 | |
| 				'resize: {width: %d, height: %d, quality: 100},',
 | |
| 				(int) $this->config['img_max_width'],
 | |
| 				(int) $this->config['img_max_height']
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		return $resize;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Checks various php.ini values and the maximum file size to determine
 | |
| 	* the maximum size chunks a file can be split up into for upload
 | |
| 	*
 | |
| 	* @return int
 | |
| 	*/
 | |
| 	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->config['max_filesize']
 | |
| 		);
 | |
| 
 | |
| 		// Use half of the maximum possible to leave plenty of room for other
 | |
| 		// POST data.
 | |
| 		return floor($max / 2);
 | |
| 	}
 | |
| 
 | |
| 	protected function temporary_filepath($file_name)
 | |
| 	{
 | |
| 		// Must preserve the extension for plupload to work.
 | |
| 		return sprintf(
 | |
| 			'%s/%s_%s%s',
 | |
| 			$this->temporary_directory,
 | |
| 			$this->config['plupload_salt'],
 | |
| 			md5($file_name),
 | |
| 			\filespec::get_extension($file_name)
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Checks whether the chunk we are about to deal with was actually uploaded
 | |
| 	* by PHP and actually exists, if not, it generates an error
 | |
| 	*
 | |
| 	* @param string $form_name The name of the file in the form data
 | |
| 	*
 | |
| 	* @return null
 | |
| 	*/
 | |
| 	protected function integrate_uploaded_file($form_name, $chunk, $file_path)
 | |
| 	{
 | |
| 		$is_multipart = $this->is_multipart();
 | |
| 		$upload = $this->request->file($form_name);
 | |
| 		if ($is_multipart && (!isset($upload['tmp_name']) || !is_uploaded_file($upload['tmp_name'])))
 | |
| 		{
 | |
| 			$this->emit_error(103, 'PLUPLOAD_ERR_MOVE_UPLOADED');
 | |
| 		}
 | |
| 
 | |
| 		$tmp_file = $this->temporary_filepath($upload['tmp_name']);
 | |
| 
 | |
| 		if (!phpbb_is_writable($this->temporary_directory) || !move_uploaded_file($upload['tmp_name'], $tmp_file))
 | |
| 		{
 | |
| 			$this->emit_error(103, 'PLUPLOAD_ERR_MOVE_UPLOADED');
 | |
| 		}
 | |
| 
 | |
| 		$out = fopen("{$file_path}.part", $chunk == 0 ? 'wb' : 'ab');
 | |
| 		if (!$out)
 | |
| 		{
 | |
| 			$this->emit_error(102, 'PLUPLOAD_ERR_OUTPUT');
 | |
| 		}
 | |
| 
 | |
| 		$in = fopen(($is_multipart) ? $tmp_file : 'php://input', 'rb');
 | |
| 		if (!$in)
 | |
| 		{
 | |
| 			$this->emit_error(101, 'PLUPLOAD_ERR_INPUT');
 | |
| 		}
 | |
| 
 | |
| 		while ($buf = fread($in, 4096))
 | |
| 		{
 | |
| 			fwrite($out, $buf);
 | |
| 		}
 | |
| 
 | |
| 		fclose($in);
 | |
| 		fclose($out);
 | |
| 
 | |
| 		if ($is_multipart)
 | |
| 		{
 | |
| 			unlink($tmp_file);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Creates the temporary directory if it does not already exist.
 | |
| 	*
 | |
| 	* @return null
 | |
| 	*/
 | |
| 	protected function prepare_temporary_directory()
 | |
| 	{
 | |
| 		if (!file_exists($this->temporary_directory))
 | |
| 		{
 | |
| 			mkdir($this->temporary_directory);
 | |
| 
 | |
| 			copy(
 | |
| 				$this->upload_directory . '/index.htm',
 | |
| 				$this->temporary_directory . '/index.htm'
 | |
| 			);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Sets the default directories for uploads
 | |
| 	*
 | |
| 	* @return null
 | |
| 	*/
 | |
| 	protected function set_default_directories()
 | |
| 	{
 | |
| 		$this->upload_directory = $this->phpbb_root_path . $this->config['upload_path'];
 | |
| 		$this->temporary_directory = $this->upload_directory . '/plupload';
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	* Sets the upload directories to the specified paths
 | |
| 	*
 | |
| 	* @param string $upload_directory Upload directory
 | |
| 	* @param string $temporary_directory Temporary directory
 | |
| 	*
 | |
| 	* @return null
 | |
| 	*/
 | |
| 	public function set_upload_directories($upload_directory, $temporary_directory)
 | |
| 	{
 | |
| 		$this->upload_directory = $upload_directory;
 | |
| 		$this->temporary_directory = $temporary_directory;
 | |
| 	}
 | |
| }
 |