Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
35.71% |
5 / 14 |
CRAP | |
23.38% |
36 / 154 |
| plupload | |
0.00% |
0 / 1 |
|
35.71% |
5 / 14 |
652.87 | |
23.38% |
36 / 154 |
| __construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
8 / 8 |
|||
| handle_upload | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 26 |
|||
| configure | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 15 |
|||
| is_active | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| is_multipart | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| emit_error | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 9 |
|||
| generate_filter_string | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 17 |
|||
| generate_resize_string | |
100.00% |
1 / 1 |
4 | |
100.00% |
12 / 12 |
|||
| get_chunk_size | |
100.00% |
1 / 1 |
4 | |
100.00% |
12 / 12 |
|||
| temporary_filepath | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 7 |
|||
| integrate_uploaded_file | |
0.00% |
0 / 1 |
156 | |
0.00% |
0 / 30 |
|||
| prepare_temporary_directory | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 9 |
|||
| set_default_directories | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
| set_upload_directories | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| <?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 \bantu\IniGetWrapper\IniGetWrapper | |
| */ | |
| 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 \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, \bantu\IniGetWrapper\IniGetWrapper $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', '', true), | |
| '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 Cache service object | |
| * @param string $forum_id The forum identifier | |
| * | |
| * @return string | |
| */ | |
| public function generate_filter_string(\phpbb\cache\service $cache, $forum_id) | |
| { | |
| $groups = []; | |
| $filters = []; | |
| $attach_extensions = $cache->obtain_attach_extensions($forum_id); | |
| unset($attach_extensions['_allowed_']); | |
| // Re-arrange the extension array to $groups[$group_name][] | |
| foreach ($attach_extensions as $extension => $extension_info) | |
| { | |
| $groups[$extension_info['group_name']]['extensions'][] = $extension; | |
| $groups[$extension_info['group_name']]['max_file_size'] = (int) $extension_info['max_filesize']; | |
| } | |
| foreach ($groups as $group => $group_info) | |
| { | |
| $filters[] = sprintf( | |
| "{title: '%s', extensions: '%s', max_file_size: %s}", | |
| addslashes(ucfirst(strtolower($group))), | |
| addslashes(implode(',', $group_info['extensions'])), | |
| $group_info['max_file_size'] | |
| ); | |
| } | |
| 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) | |
| { | |
| $preserve_headers_value = $this->config['img_strip_metadata'] ? 'false' : 'true'; | |
| $resize = sprintf( | |
| 'resize: {width: %d, height: %d, quality: %d, preserve_headers: %s},', | |
| (int) $this->config['img_max_width'], | |
| (int) $this->config['img_max_height'], | |
| (int) $this->config['img_quality'], | |
| $preserve_headers_value | |
| ); | |
| } | |
| return $resize; | |
| } | |
| /** | |
| * Checks various php.ini values to determine the maximum chunk | |
| * size a file should be split into for upload. | |
| * | |
| * The intention is to calculate a value which reflects whatever | |
| * the most restrictive limit is set to. And to then set the chunk | |
| * size to half that value, to ensure any required transfer overhead | |
| * and POST data remains well within the limit. Or, if all of the | |
| * limits are set to unlimited, the chunk size will also be unlimited. | |
| * | |
| * @return int | |
| * | |
| * @access public | |
| */ | |
| public function get_chunk_size() | |
| { | |
| $max = 0; | |
| $limits = [ | |
| $this->php_ini->getBytes('memory_limit'), | |
| $this->php_ini->getBytes('upload_max_filesize'), | |
| $this->php_ini->getBytes('post_max_size'), | |
| ]; | |
| foreach ($limits as $limit_type) | |
| { | |
| if ($limit_type > 0) | |
| { | |
| $max = ($max !== 0) ? min($limit_type, $max) : $limit_type; | |
| } | |
| } | |
| 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), | |
| \phpbb\files\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; | |
| } | |
| } |