Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
93.33% covered (success)
93.33%
14 / 15
CRAP
98.06% covered (success)
98.06%
101 / 103
upload
0.00% covered (danger)
0.00%
0 / 1
93.33% covered (success)
93.33%
14 / 15
54
98.06% covered (success)
98.06%
101 / 103
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
6 / 6
 reset_vars
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
6 / 6
 set_allowed_extensions
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
3 / 3
 set_allowed_dimensions
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 set_max_filesize
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
3 / 3
 set_disallowed_content
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
3 / 3
 set_error_prefix
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 2
 handle_upload
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 assign_internal_error
100.00% covered (success)
100.00%
1 / 1
12
100.00% covered (success)
100.00%
29 / 29
 common_checks
100.00% covered (success)
100.00%
1 / 1
7
100.00% covered (success)
100.00%
10 / 10
 valid_extension
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 valid_dimensions
100.00% covered (success)
100.00%
1 / 1
13
100.00% covered (success)
100.00%
8 / 8
 is_valid
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
2 / 2
 valid_content
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 image_types
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
19 / 19
<?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 language Language class */
    protected $language;
    /** @var request_interface Request class */
    protected $request;
    /**
     * 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
     */
    public function __construct(filesystem_interface $filesystem, factory $factory, language $language, \bantu\IniGetWrapper\IniGetWrapper $php_ini, request_interface $request)
    {
        $this->filesystem = $filesystem;
        $this->factory = $factory;
        $this->language = $language;
        $this->php_ini = $php_ini;
        $this->request = $request;
    }
    /**
     * 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 = [
            IMAGETYPE_GIF        => ['gif'],
            IMAGETYPE_JPEG        => ['jpg', 'jpeg'],
            IMAGETYPE_PNG        => ['png'],
            IMAGETYPE_SWF        => ['swf'],
            IMAGETYPE_PSD        => ['psd'],
            IMAGETYPE_BMP        => ['bmp'],
            IMAGETYPE_TIFF_II    => ['tif', 'tiff'],
            IMAGETYPE_TIFF_MM    => ['tif', 'tiff'],
            IMAGETYPE_JPC        => ['jpg', 'jpeg'],
            IMAGETYPE_JP2        => ['jpg', 'jpeg'],
            IMAGETYPE_JPX        => ['jpg', 'jpeg'],
            IMAGETYPE_JB2        => ['jpg', 'jpeg'],
            IMAGETYPE_IFF        => ['iff'],
            IMAGETYPE_WBMP        => ['wbmp'],
            IMAGETYPE_XBM        => ['xbm'],
            IMAGETYPE_WEBP        => ['webp'],
        ];
        if (defined('IMAGETYPE_SWC'))
        {
            $result[IMAGETYPE_SWC] = ['swc'];
        }
        return $result;
    }
}