Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
12.50% covered (danger)
12.50%
1 / 8
CRAP
6.21% covered (danger)
6.21%
9 / 145
upload
0.00% covered (danger)
0.00%
0 / 1
12.50% covered (danger)
12.50%
1 / 8
1360.18
6.21% covered (danger)
6.21%
9 / 145
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
9 / 9
 get_data
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 5
 prepare_form
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 8
 process_form
0.00% covered (danger)
0.00%
0 / 1
552
0.00% covered (danger)
0.00%
0 / 99
 prepare_form_acp
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 4
 delete
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 18
 get_template_name
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 can_upload
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 1
<?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\avatar\driver;
/**
* Handles avatars uploaded to the board
*/
class upload extends \phpbb\avatar\driver\driver
{
    /**
     * @var \phpbb\filesystem\filesystem_interface
     */
    protected $filesystem;
    /**
    * @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 $filesystem phpBB filesystem helper
    * @param \phpbb\path_helper $path_helper phpBB path helper
    * @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\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->dispatcher = $dispatcher;
        $this->files_factory = $files_factory;
        $this->cache = $cache;
    }
    /**
    * {@inheritdoc}
    */
    public function get_data($row)
    {
        $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $this->path_helper->get_web_root_path();
        return array(
            'src' => $root_path . 'download/file.' . $this->php_ext . '?avatar=' . $row['avatar'],
            'width' => $row['avatar_width'],
            'height' => $row['avatar_height'],
        );
    }
    /**
    * {@inheritdoc}
    */
    public function prepare_form($request, $template, $user, $row, &$error)
    {
        if (!$this->can_upload())
        {
            return false;
        }
        $template->assign_vars(array(
            'S_UPLOAD_AVATAR_URL' => ($this->config['allow_avatar_remote_upload']) ? true : false,
            'AVATAR_UPLOAD_SIZE' => $this->config['avatar_filesize'],
        ));
        return true;
    }
    /**
    * {@inheritdoc}
    */
    public function process_form($request, $template, $user, $row, &$error)
    {
        if (!$this->can_upload())
        {
            return 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->handle_upload('files.types.form', 'avatar_upload_file');
        }
        else if (!empty($this->config['allow_avatar_remote_upload']) && !empty($url))
        {
            if (!preg_match('#^(http|https|ftp)://#i', $url))
            {
                $url = 'http://' . $url;
            }
            if (!function_exists('validate_data'))
            {
                require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
            }
            $validate_array = validate_data(
                array(
                    'url' => $url,
                ),
                array(
                    'url' => array('string', true, 5, 255),
                )
            );
            $error = array_merge($error, $validate_array);
            if (!empty($error))
            {
                return false;
            }
            // Do not allow specifying the port (see RFC 3986) or IP addresses
            // remote_upload() will do its own check for allowed filetypes
            if (!preg_match('#^(http|https|ftp)://(?:(.*?\.)*?[a-z0-9\-]+?\.[a-z]{2,4}|(?:\d{1,3}\.){3,5}\d{1,3}):?([0-9]*?).*?\.('. implode('|', $this->allowed_extensions) . ')$#i', $url) ||
                preg_match('@^(http|https|ftp)://[^/:?#]+:[0-9]+[/:?#]@i', $url) ||
                preg_match('#^(http|https|ftp)://(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])#i', $url) ||
                preg_match('#^(http|https|ftp)://(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){0,5}(?:[\dA-F]{1,4}(?::[\dA-F]{1,4})?|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:)|(?:::))#i', $url))
            {
                $error[] = 'AVATAR_URL_INVALID';
                return false;
            }
            $file = $upload->handle_upload('files.types.remote', $url);
        }
        else
        {
            return false;
        }
        $prefix = $this->config['avatar_salt'] . '_';
        $file->clean_filename('avatar', $prefix, $row['id']);
        // If there was an error during upload, then abort operation
        if (count($file->error))
        {
            $file->remove();
            $error = $file->error;
            return false;
        }
        // Calculate new destination
        $destination = $this->config['avatar_path'];
        // Adjust destination path (no trailing slash)
        if (substr($destination, -1, 1) == '/' || substr($destination, -1, 1) == '\\')
        {
            $destination = substr($destination, 0, -1);
        }
        $destination = str_replace(array('../', '..\\', './', '.\\'), '', $destination);
        if ($destination && ($destination[0] == '/' || $destination[0] == "\\"))
        {
            $destination = '';
        }
        $filedata = array(
            'filename'            => $file->get('filename'),
            'filesize'            => $file->get('filesize'),
            'mimetype'            => $file->get('mimetype'),
            'extension'            => $file->get('extension'),
            'physical_filename'    => $file->get('realname'),
            'real_filename'        => $file->get('uploadname'),
        );
        /**
        * Before moving new file in place (and eventually overwriting the existing avatar with the newly uploaded avatar)
        *
        * @event core.avatar_driver_upload_move_file_before
        * @var    array    filedata            Array containing uploaded file data
        * @var    \phpbb\files\filespec file    Instance of filespec class
        * @var    string    destination            Destination directory where the file is going to be moved
        * @var    string    prefix                Prefix for the avatar filename
        * @var    array    row                    Array with avatar row data
        * @var    array    error                Array of errors, if filled in by this event file will not be moved
        * @since 3.1.6-RC1
        * @changed 3.1.9-RC1 Added filedata
        * @changed 3.2.3-RC1 Added file
        */
        $vars = array(
            'filedata',
            'file',
            'destination',
            'prefix',
            'row',
            'error',
        );
        extract($this->dispatcher->trigger_event('core.avatar_driver_upload_move_file_before', compact($vars)));
        unset($filedata);
        if (!count($error))
        {
            // Move file and overwrite any existing image
            $file->move_file($destination, true);
        }
        // If there was an error during move, then clean up leftovers
        $error = array_merge($error, $file->error);
        if (count($error))
        {
            $file->remove();
            return false;
        }
        // Delete current avatar if not overwritten
        $ext = substr(strrchr($row['avatar'], '.'), 1);
        if ($ext && $ext !== $file->get('extension'))
        {
            $this->delete($row);
        }
        return array(
            'avatar' => $row['id'] . '_' . time() . '.' . $file->get('extension'),
            'avatar_width' => $file->get('width'),
            'avatar_height' => $file->get('height'),
        );
    }
    /**
    * {@inheritdoc}
    */
    public function prepare_form_acp($user)
    {
        return array(
            'allow_avatar_remote_upload'=> array('lang' => 'ALLOW_REMOTE_UPLOAD', 'validate' => 'bool',    'type' => 'radio:yes_no', 'explain' => true),
            'avatar_filesize'        => array('lang' => 'MAX_FILESIZE',            'validate' => 'int:0',    'type' => 'number:0', 'explain' => true, 'append' => ' ' . $user->lang['BYTES']),
            'avatar_path'            => array('lang' => 'AVATAR_STORAGE_PATH',    'validate' => 'rpath',    'type' => 'text:20:255', 'explain' => true),
        );
    }
    /**
    * {@inheritdoc}
    */
    public function delete($row)
    {
        $error = array();
        $destination = $this->config['avatar_path'];
        $prefix = $this->config['avatar_salt'] . '_';
        $ext = substr(strrchr($row['avatar'], '.'), 1);
        $filename = $this->phpbb_root_path . $destination . '/' . $prefix . $row['id'] . '.' . $ext;
        /**
        * Before deleting an existing avatar
        *
        * @event core.avatar_driver_upload_delete_before
        * @var    string    destination            Destination directory where the file is going to be deleted
        * @var    string    prefix                Prefix for the avatar filename
        * @var    array    row                    Array with avatar row data
        * @var    array    error                Array of errors, if filled in by this event file will not be deleted
        * @since 3.1.6-RC1
        */
        $vars = array(
            'destination',
            'prefix',
            'row',
            'error',
        );
        extract($this->dispatcher->trigger_event('core.avatar_driver_upload_delete_before', compact($vars)));
        if (!count($error) && $this->filesystem->exists($filename))
        {
            try
            {
                $this->filesystem->remove($filename);
                return true;
            }
            catch (\phpbb\filesystem\exception\filesystem_exception $e)
            {
                // Fail is covered by return statement below
            }
        }
        return false;
    }
    /**
    * {@inheritdoc}
    */
    public function get_template_name()
    {
        return 'ucp_avatar_options_upload.html';
    }
    /**
    * Check if user is able to upload an avatar
    *
    * @return bool True if user can upload, false if not
    */
    protected function can_upload()
    {
        return ($this->filesystem->exists($this->phpbb_root_path . $this->config['avatar_path']) && $this->filesystem->is_writable($this->phpbb_root_path . $this->config['avatar_path']) && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on'));
    }
}