diff options
Diffstat (limited to 'phpBB/phpbb/avatar')
| -rw-r--r-- | phpBB/phpbb/avatar/driver/driver.php | 131 | ||||
| -rw-r--r-- | phpBB/phpbb/avatar/driver/driver_interface.php | 113 | ||||
| -rw-r--r-- | phpBB/phpbb/avatar/driver/gravatar.php | 185 | ||||
| -rw-r--r-- | phpBB/phpbb/avatar/driver/local.php | 203 | ||||
| -rw-r--r-- | phpBB/phpbb/avatar/driver/remote.php | 221 | ||||
| -rw-r--r-- | phpBB/phpbb/avatar/driver/upload.php | 216 | ||||
| -rw-r--r-- | phpBB/phpbb/avatar/manager.php | 366 | 
7 files changed, 1435 insertions, 0 deletions
diff --git a/phpBB/phpbb/avatar/driver/driver.php b/phpBB/phpbb/avatar/driver/driver.php new file mode 100644 index 0000000000..b3ced7edf7 --- /dev/null +++ b/phpBB/phpbb/avatar/driver/driver.php @@ -0,0 +1,131 @@ +<?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; + +/** +* Base class for avatar drivers +*/ +abstract class driver implements \phpbb\avatar\driver\driver_interface +{ +	/** +	* Avatar driver name +	* @var string +	*/ +	protected $name; + +	/** +	* Current board configuration +	* @var \phpbb\config\config +	*/ +	protected $config; + +	/** +	* Current $phpbb_root_path +	* @var string +	*/ +	protected $phpbb_root_path; + +	/** +	* Current $php_ext +	* @var string +	*/ +	protected $php_ext; + +	/** +	* Path Helper +	* @var \phpbb\path_helper +	*/ +	protected $path_helper; + +	/** +	* Cache driver +	* @var \phpbb\cache\driver\driver_interface +	*/ +	protected $cache; + +	/** +	* Array of allowed avatar image extensions +	* Array is used for setting the allowed extensions in the fileupload class +	* and as a base for a regex of allowed extensions, which will be formed by +	* imploding the array with a "|". +	* +	* @var array +	*/ +	protected $allowed_extensions = array( +		'gif', +		'jpg', +		'jpeg', +		'png', +	); + +	/** +	* 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\path_helper $path_helper phpBB path helper +	* @param \phpbb\cache\driver\driver_interface $cache Cache driver +	*/ +	public function __construct(\phpbb\config\config $config, $phpbb_root_path, $php_ext, \phpbb\path_helper $path_helper, \phpbb\cache\driver\driver_interface $cache = null) +	{ +		$this->config = $config; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; +		$this->path_helper = $path_helper; +		$this->cache = $cache; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_custom_html($user, $row, $alt = '') +	{ +		return ''; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function prepare_form_acp($user) +	{ +		return array(); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function delete($row) +	{ +		return true; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_name() +	{ +		return $this->name; +	} + +	/** +	* Sets the name of the driver. +	* +	* @param string	$name Driver name +	*/ +	public function set_name($name) +	{ +		$this->name = $name; +	} +} diff --git a/phpBB/phpbb/avatar/driver/driver_interface.php b/phpBB/phpbb/avatar/driver/driver_interface.php new file mode 100644 index 0000000000..835609745a --- /dev/null +++ b/phpBB/phpbb/avatar/driver/driver_interface.php @@ -0,0 +1,113 @@ +<?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; + +/** +* Interface for avatar drivers +*/ +interface driver_interface +{ +	/** +	* Returns the name of the driver. +	* +	* @return string	Name of driver. +	*/ +	public function get_name(); + +	/** +	* Get the avatar url and dimensions +	* +	* @param array	$row User data or group data that has been cleaned with +	*        \phpbb\avatar\manager::clean_row +	* @return array Avatar data, must have keys src, width and height, e.g. +	*        ['src' => '', 'width' => 0, 'height' => 0] +	*/ +	public function get_data($row); + +	/** +	* Returns custom html if it is needed for displaying this avatar +	* +	* @param \phpbb\user $user phpBB user object +	* @param array	$row User data or group data that has been cleaned with +	*        \phpbb\avatar\manager::clean_row +	* @param string $alt Alternate text for avatar image +	* +	* @return string HTML +	*/ +	public function get_custom_html($user, $row, $alt = ''); + +	/** +	* Prepare form for changing the settings of this avatar +	* +	* @param \phpbb\request\request $request Request object +	* @param \phpbb\template\template	$template Template object +	* @param \phpbb\user $user User object +	* @param array	$row User data or group data that has been cleaned with +	*        \phpbb\avatar\manager::clean_row +	* @param array	&$error Reference to an error array that is filled by this +	*        function. Key values can either be a string with a language key or +	*        an array that will be passed to vsprintf() with the language key in +	*        the first array key. +	* +	* @return bool True if form has been successfully prepared +	*/ +	public function prepare_form($request, $template, $user, $row, &$error); + +	/** +	* Prepare form for changing the acp settings of this avatar +	* +	* @param \phpbb\user $user phpBB user object +	* +	* @return array Array of configuration options as consumed by acp_board. +	*        The setting for enabling/disabling the avatar will be handled by +	*        the avatar manager. +	*/ +	public function prepare_form_acp($user); + +	/** +	* Process form data +	* +	* @param \phpbb\request\request $request Request object +	* @param \phpbb\template\template	$template Template object +	* @param \phpbb\user $user User object +	* @param array	$row User data or group data that has been cleaned with +	*        \phpbb\avatar\manager::clean_row +	* @param array	&$error Reference to an error array that is filled by this +	*        function. Key values can either be a string with a language key or +	*        an array that will be passed to vsprintf() with the language key in +	*        the first array key. +	* +	* @return array Array containing the avatar data as follows: +	*        ['avatar'], ['avatar_width'], ['avatar_height'] +	*/ +	public function process_form($request, $template, $user, $row, &$error); + +	/** +	* Delete avatar +	* +	* @param array $row User data or group data that has been cleaned with +	*        \phpbb\avatar\manager::clean_row +	* +	* @return bool True if avatar has been deleted or there is no need to delete, +	*        i.e. when the avatar is not hosted locally. +	*/ +	public function delete($row); + +	/** +	* Get the avatar driver's template name +	* +	* @return string Avatar driver's template name +	*/ +	public function get_template_name(); +} diff --git a/phpBB/phpbb/avatar/driver/gravatar.php b/phpBB/phpbb/avatar/driver/gravatar.php new file mode 100644 index 0000000000..2082e0fd02 --- /dev/null +++ b/phpBB/phpbb/avatar/driver/gravatar.php @@ -0,0 +1,185 @@ +<?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 hosted at gravatar.com +*/ +class gravatar extends \phpbb\avatar\driver\driver +{ +	/** +	* The URL for the gravatar service +	*/ +	const GRAVATAR_URL = '//secure.gravatar.com/avatar/'; + +	/** +	* {@inheritdoc} +	*/ +	public function get_data($row) +	{ +		return array( +			'src' => $row['avatar'], +			'width' => $row['avatar_width'], +			'height' => $row['avatar_height'], +		); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_custom_html($user, $row, $alt = '') +	{ +		return '<img src="' . $this->get_gravatar_url($row) . '" ' . +			($row['avatar_width'] ? ('width="' . $row['avatar_width'] . '" ') : '') . +			($row['avatar_height'] ? ('height="' . $row['avatar_height'] . '" ') : '') . +			'alt="' . ((!empty($user->lang[$alt])) ? $user->lang[$alt] : $alt) . '" />'; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function prepare_form($request, $template, $user, $row, &$error) +	{ +		$template->assign_vars(array( +			'AVATAR_GRAVATAR_WIDTH' => (($row['avatar_type'] == $this->get_name() || $row['avatar_type'] == 'gravatar') && $row['avatar_width']) ? $row['avatar_width'] : $request->variable('avatar_gravatar_width', 0), +			'AVATAR_GRAVATAR_HEIGHT' => (($row['avatar_type'] == $this->get_name() || $row['avatar_type'] == 'gravatar') && $row['avatar_height']) ? $row['avatar_height'] : $request->variable('avatar_gravatar_width', 0), +			'AVATAR_GRAVATAR_EMAIL' => (($row['avatar_type'] == $this->get_name() || $row['avatar_type'] == 'gravatar') && $row['avatar']) ? $row['avatar'] : '', +		)); + +		return true; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function process_form($request, $template, $user, $row, &$error) +	{ +		$row['avatar'] = $request->variable('avatar_gravatar_email', ''); +		$row['avatar_width'] = $request->variable('avatar_gravatar_width', 0); +		$row['avatar_height'] = $request->variable('avatar_gravatar_height', 0); + +		if (empty($row['avatar'])) +		{ +			return false; +		} + +		if (!function_exists('validate_data')) +		{ +			require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); +		} + +		$validate_array = validate_data( +			array( +				'email' => $row['avatar'], +			), +			array( +				'email' => array( +					array('string', false, 6, 60), +					array('email'), +				), +			) +		); + +		$error = array_merge($error, $validate_array); + +		if (!empty($error)) +		{ +			return false; +		} + +		// Make sure getimagesize works... +		if (function_exists('getimagesize') && ($row['avatar_width'] <= 0 || $row['avatar_height'] <= 0)) +		{ +			/** +			* default to the minimum of the maximum allowed avatar size if the size +			* is not or only partially entered +			*/ +			$row['avatar_width'] = $row['avatar_height'] = min($this->config['avatar_max_width'], $this->config['avatar_max_height']); +			$url = $this->get_gravatar_url($row); + +			if (($row['avatar_width'] <= 0 || $row['avatar_height'] <= 0) && (($image_data = getimagesize($url)) === false)) +			{ +				$error[] = 'UNABLE_GET_IMAGE_SIZE'; +				return false; +			} + +			if (!empty($image_data) && ($image_data[0] <= 0 || $image_data[1] <= 0)) +			{ +				$error[] = 'AVATAR_NO_SIZE'; +				return false; +			} + +			$row['avatar_width'] = ($row['avatar_width'] && $row['avatar_height']) ? $row['avatar_width'] : $image_data[0]; +			$row['avatar_height'] = ($row['avatar_width'] && $row['avatar_height']) ? $row['avatar_height'] : $image_data[1]; +		} + +		if ($row['avatar_width'] <= 0 || $row['avatar_height'] <= 0) +		{ +			$error[] = 'AVATAR_NO_SIZE'; +			return false; +		} + +		if ($this->config['avatar_max_width'] || $this->config['avatar_max_height']) +		{ +			if ($row['avatar_width'] > $this->config['avatar_max_width'] || $row['avatar_height'] > $this->config['avatar_max_height']) +			{ +				$error[] = array('AVATAR_WRONG_SIZE', $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], $row['avatar_width'], $row['avatar_height']); +				return false; +			} +		} + +		if ($this->config['avatar_min_width'] || $this->config['avatar_min_height']) +		{ +			if ($row['avatar_width'] < $this->config['avatar_min_width'] || $row['avatar_height'] < $this->config['avatar_min_height']) +			{ +				$error[] = array('AVATAR_WRONG_SIZE', $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], $row['avatar_width'], $row['avatar_height']); +				return false; +			} +		} + +		return array( +			'avatar' => $row['avatar'], +			'avatar_width' => $row['avatar_width'], +			'avatar_height' => $row['avatar_height'], +		); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_template_name() +	{ +		return 'ucp_avatar_options_gravatar.html'; +	} + +	/** +	* Build gravatar URL for output on page +	* +	* @param array $row User data or group data that has been cleaned with +	*        \phpbb\avatar\manager::clean_row +	* @return string Gravatar URL +	*/ +	protected function get_gravatar_url($row) +	{ +		$url = self::GRAVATAR_URL; +		$url .=  md5(strtolower(trim($row['avatar']))); + +		if ($row['avatar_width'] || $row['avatar_height']) +		{ +			$url .= '?s=' . max($row['avatar_width'], $row['avatar_height']); +		} + +		return $url; +	} +} diff --git a/phpBB/phpbb/avatar/driver/local.php b/phpBB/phpbb/avatar/driver/local.php new file mode 100644 index 0000000000..36087f8ba0 --- /dev/null +++ b/phpBB/phpbb/avatar/driver/local.php @@ -0,0 +1,203 @@ +<?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 selected from the board gallery +*/ +class local extends \phpbb\avatar\driver\driver +{ +	/** +	* {@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 . $this->config['avatar_gallery_path'] . '/' . $row['avatar'], +			'width' => $row['avatar_width'], +			'height' => $row['avatar_height'], +		); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function prepare_form($request, $template, $user, $row, &$error) +	{ +		$avatar_list = $this->get_avatar_list($user); +		$category = $request->variable('avatar_local_cat', key($avatar_list)); + +		foreach ($avatar_list as $cat => $null) +		{ +			if (!empty($avatar_list[$cat])) +			{ +				$template->assign_block_vars('avatar_local_cats', array( +					'NAME' => $cat, +					'SELECTED' => ($cat == $category), +				)); +			} + +			if ($cat != $category) +			{ +				unset($avatar_list[$cat]); +			} +		} + +		if (!empty($avatar_list[$category])) +		{ +			$template->assign_vars(array( +				'AVATAR_LOCAL_SHOW' => true, +			)); + +			$table_cols = isset($row['avatar_gallery_cols']) ? $row['avatar_gallery_cols'] : 4; +			$row_count = $col_count = $avatar_pos = 0; +			$avatar_count = sizeof($avatar_list[$category]); + +			reset($avatar_list[$category]); + +			while ($avatar_pos < $avatar_count) +			{ +				$img = current($avatar_list[$category]); +				next($avatar_list[$category]); + +				if ($col_count == 0) +				{ +					++$row_count; +					$template->assign_block_vars('avatar_local_row', array( +					)); +				} + +				$template->assign_block_vars('avatar_local_row.avatar_local_col', array( +					'AVATAR_IMAGE'  => $this->phpbb_root_path . $this->config['avatar_gallery_path'] . '/' . $img['file'], +					'AVATAR_NAME' 	=> $img['name'], +					'AVATAR_FILE' 	=> $img['filename'], +				)); + +				$template->assign_block_vars('avatar_local_row.avatar_local_option', array( +					'AVATAR_FILE' 		=> $img['filename'], +					'S_OPTIONS_AVATAR'	=> $img['filename'] +				)); + +				$col_count = ($col_count + 1) % $table_cols; + +				++$avatar_pos; +			} +		} + +		return true; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function prepare_form_acp($user) +	{ +		return array( +			'avatar_gallery_path'	=> array('lang' => 'AVATAR_GALLERY_PATH',	'validate' => 'rpath',	'type' => 'text:20:255', 'explain' => true), +		); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function process_form($request, $template, $user, $row, &$error) +	{ +		$avatar_list = $this->get_avatar_list($user); +		$category = $request->variable('avatar_local_cat', ''); + +		$file = $request->variable('avatar_local_file', ''); + +		if (empty($category) || empty($file)) +		{ +			return false; +		} + +		if (!isset($avatar_list[$category][urldecode($file)])) +		{ +			$error[] = 'AVATAR_URL_NOT_FOUND'; +			return false; +		} + +		return array( +			'avatar' => ($category != $user->lang['NO_AVATAR_CATEGORY']) ? $category . '/' . $file : $file, +			'avatar_width' => $avatar_list[$category][urldecode($file)]['width'], +			'avatar_height' => $avatar_list[$category][urldecode($file)]['height'], +		); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_template_name() +	{ +		return 'ucp_avatar_options_local.html'; +	} + +	/** +	* Get a list of avatars that are locally available +	* Results get cached for 24 hours (86400 seconds) +	* +	* @param \phpbb\user $user User object +	* +	* @return array Array containing the locally available avatars +	*/ +	protected function get_avatar_list($user) +	{ +		$avatar_list = ($this->cache == null) ? false : $this->cache->get('_avatar_local_list'); + +		if ($avatar_list === false) +		{ +			$avatar_list = array(); +			$path = $this->phpbb_root_path . $this->config['avatar_gallery_path']; + +			$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS), \RecursiveIteratorIterator::SELF_FIRST); +			foreach ($iterator as $file_info) +			{ +				$file_path = $file_info->getPath(); +				$image = $file_info->getFilename(); + +				// Match all images in the gallery folder +				if (preg_match('#^[^&\'"<>]+\.(?:' . implode('|', $this->allowed_extensions) . ')$#i', $image) && is_file($file_path . '/' . $image)) +				{ +					if (function_exists('getimagesize')) +					{ +						$dims = getimagesize($file_path . '/' . $image); +					} +					else +					{ +						$dims = array(0, 0); +					} +					$cat = ($path == $file_path) ? $user->lang['NO_AVATAR_CATEGORY'] : str_replace("$path/", '', $file_path); +					$avatar_list[$cat][$image] = array( +						'file'      => ($cat != $user->lang['NO_AVATAR_CATEGORY']) ? rawurlencode($cat) . '/' . rawurlencode($image) : rawurlencode($image), +						'filename'  => rawurlencode($image), +						'name'      => ucfirst(str_replace('_', ' ', preg_replace('#^(.*)\..*$#', '\1', $image))), +						'width'     => $dims[0], +						'height'    => $dims[1], +					); +				} +			} +			ksort($avatar_list); + +			if ($this->cache != null) +			{ +				$this->cache->put('_avatar_local_list', $avatar_list, 86400); +			} +		} + +		return $avatar_list; +	} +} diff --git a/phpBB/phpbb/avatar/driver/remote.php b/phpBB/phpbb/avatar/driver/remote.php new file mode 100644 index 0000000000..4b0ee3f06f --- /dev/null +++ b/phpBB/phpbb/avatar/driver/remote.php @@ -0,0 +1,221 @@ +<?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 hosted remotely +*/ +class remote extends \phpbb\avatar\driver\driver +{ +	/** +	* {@inheritdoc} +	*/ +	public function get_data($row) +	{ +		return array( +			'src' => $row['avatar'], +			'width' => $row['avatar_width'], +			'height' => $row['avatar_height'], +		); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function prepare_form($request, $template, $user, $row, &$error) +	{ +		$template->assign_vars(array( +			'AVATAR_REMOTE_WIDTH' => ((in_array($row['avatar_type'], array(AVATAR_REMOTE, $this->get_name(), 'remote'))) && $row['avatar_width']) ? $row['avatar_width'] : $request->variable('avatar_remote_width', 0), +			'AVATAR_REMOTE_HEIGHT' => ((in_array($row['avatar_type'], array(AVATAR_REMOTE, $this->get_name(), 'remote'))) && $row['avatar_height']) ? $row['avatar_height'] : $request->variable('avatar_remote_width', 0), +			'AVATAR_REMOTE_URL' => ((in_array($row['avatar_type'], array(AVATAR_REMOTE, $this->get_name(), 'remote'))) && $row['avatar']) ? $row['avatar'] : '', +		)); + +		return true; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function process_form($request, $template, $user, $row, &$error) +	{ +		$url = $request->variable('avatar_remote_url', ''); +		$width = $request->variable('avatar_remote_width', 0); +		$height = $request->variable('avatar_remote_height', 0); + +		if (empty($url)) +		{ +			return false; +		} + +		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; +		} + +		// Check if this url looks alright +		// This isn't perfect, but it's what phpBB 3.0 did, and might as well make sure everything is compatible +		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)) +		{ +			$error[] = 'AVATAR_URL_INVALID'; +			return false; +		} + +		// Make sure getimagesize works... +		if (function_exists('getimagesize')) +		{ +			if (($width <= 0 || $height <= 0) && (($image_data = @getimagesize($url)) === false)) +			{ +				$error[] = 'UNABLE_GET_IMAGE_SIZE'; +				return false; +			} + +			if (!empty($image_data) && ($image_data[0] <= 0 || $image_data[1] <= 0)) +			{ +				$error[] = 'AVATAR_NO_SIZE'; +				return false; +			} + +			$width = ($width && $height) ? $width : $image_data[0]; +			$height = ($width && $height) ? $height : $image_data[1]; +		} + +		if ($width <= 0 || $height <= 0) +		{ +			$error[] = 'AVATAR_NO_SIZE'; +			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)); + +		// Check if this is actually an image +		if ($file_stream = @fopen($url, 'r')) +		{ +			// Timeout after 1 second +			stream_set_timeout($file_stream, 1); +			// read some data to ensure headers are present +			fread($file_stream, 1024); +			$meta = stream_get_meta_data($file_stream); + +			if (isset($meta['wrapper_data']['headers']) && is_array($meta['wrapper_data']['headers'])) +			{ +				$headers = $meta['wrapper_data']['headers']; +			} +			else if (isset($meta['wrapper_data']) && is_array($meta['wrapper_data'])) +			{ +				$headers = $meta['wrapper_data']; +			} +			else +			{ +				$headers = array(); +			} + +			foreach ($headers as $header) +			{ +				$header = preg_split('/ /', $header, 2); +				if (strtr(strtolower(trim($header[0], ':')), '_', '-') === 'content-type') +				{ +					if (strpos($header[1], 'image/') !== 0) +					{ +						$error[] = 'AVATAR_URL_INVALID'; +						fclose($file_stream); +						return false; +					} +					else +					{ +						fclose($file_stream); +						break; +					} +				} +			} +		} +		else +		{ +			$error[] = 'AVATAR_URL_INVALID'; +			return false; +		} + +		if (!empty($image_data) && (!isset($types[$image_data[2]]) || !in_array($extension, $types[$image_data[2]]))) +		{ +			if (!isset($types[$image_data[2]])) +			{ +				$error[] = 'UNABLE_GET_IMAGE_SIZE'; +			} +			else +			{ +				$error[] = array('IMAGE_FILETYPE_MISMATCH', $types[$image_data[2]][0], $extension); +			} + +			return false; +		} + +		if ($this->config['avatar_max_width'] || $this->config['avatar_max_height']) +		{ +			if ($width > $this->config['avatar_max_width'] || $height > $this->config['avatar_max_height']) +			{ +				$error[] = array('AVATAR_WRONG_SIZE', $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], $width, $height); +				return false; +			} +		} + +		if ($this->config['avatar_min_width'] || $this->config['avatar_min_height']) +		{ +			if ($width < $this->config['avatar_min_width'] || $height < $this->config['avatar_min_height']) +			{ +				$error[] = array('AVATAR_WRONG_SIZE', $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], $width, $height); +				return false; +			} +		} + +		return array( +			'avatar' => $url, +			'avatar_width' => $width, +			'avatar_height' => $height, +		); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_template_name() +	{ +		return 'ucp_avatar_options_remote.html'; +	} +} diff --git a/phpBB/phpbb/avatar/driver/upload.php b/phpBB/phpbb/avatar/driver/upload.php new file mode 100644 index 0000000000..ee36243844 --- /dev/null +++ b/phpBB/phpbb/avatar/driver/upload.php @@ -0,0 +1,216 @@ +<?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\mimetype\guesser +	*/ +	protected $mimetype_guesser; + +	/** +	* 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_path_helper $path_helper phpBB path helper +	* @param \phpbb\mimetype\guesser $mimetype_guesser Mimetype guesser +	* @param \phpbb\cache\driver\driver_interface $cache Cache driver +	*/ +	public function __construct(\phpbb\config\config $config, $phpbb_root_path, $php_ext, \phpbb\path_helper $path_helper, \phpbb\mimetype\guesser $mimetype_guesser, \phpbb\cache\driver\driver_interface $cache = null) +	{ +		$this->config = $config; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; +		$this->path_helper = $path_helper; +		$this->mimetype_guesser = $mimetype_guesser; +		$this->cache = $cache; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_data($row, $ignore_config = false) +	{ +		$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; +		} + +		if (!class_exists('fileupload')) +		{ +			include($this->phpbb_root_path . 'includes/functions_upload.' . $this->php_ext); +		} + +		$upload = new \fileupload('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)); + +		$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); +		} +		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; +			} + +			$file = $upload->remote_upload($url, $this->mimetype_guesser); +		} +		else +		{ +			return false; +		} + +		$prefix = $this->config['avatar_salt'] . '_'; +		$file->clean_filename('avatar', $prefix, $row['id']); + +		$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 = ''; +		} + +		// Move file and overwrite any existing image +		$file->move_file($destination, true); + +		if (sizeof($file->error)) +		{ +			$file->remove(); +			$error = array_merge($error, $file->error); +			return false; +		} + +		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) +	{ +		$ext = substr(strrchr($row['avatar'], '.'), 1); +		$filename = $this->phpbb_root_path . $this->config['avatar_path'] . '/' . $this->config['avatar_salt'] . '_' . $row['id'] . '.' . $ext; + +		if (file_exists($filename)) +		{ +			@unlink($filename); +		} + +		return true; +	} + +	/** +	* {@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 (file_exists($this->phpbb_root_path . $this->config['avatar_path']) && phpbb_is_writable($this->phpbb_root_path . $this->config['avatar_path']) && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on')); +	} +} diff --git a/phpBB/phpbb/avatar/manager.php b/phpBB/phpbb/avatar/manager.php new file mode 100644 index 0000000000..8d83152ed6 --- /dev/null +++ b/phpBB/phpbb/avatar/manager.php @@ -0,0 +1,366 @@ +<?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; + +class manager +{ +	/** +	* phpBB configuration +	* @var \phpbb\config\config +	*/ +	protected $config; + +	/** +	* Array that contains a list of enabled drivers +	* @var array +	*/ +	static protected $enabled_drivers = false; + +	/** +	* Array that contains all available avatar drivers which are passed via the +	* service container +	* @var array +	*/ +	protected $avatar_drivers; + +	/** +	* Default avatar data row +	* @var array +	*/ +	static protected $default_row = array( +		'avatar'		=> '', +		'avatar_type'	=> '', +		'avatar_width'	=> 0, +		'avatar_height'	=> 0, +	); + +	/** +	* Construct an avatar manager object +	* +	* @param \phpbb\config\config $config phpBB configuration +	* @param array $avatar_drivers Avatar drivers passed via the service container +	*/ +	public function __construct(\phpbb\config\config $config, $avatar_drivers) +	{ +		$this->config = $config; +		$this->register_avatar_drivers($avatar_drivers); +	} + +	/** +	* Register avatar drivers +	* +	* @param array $avatar_drivers Service collection of avatar drivers +	*/ +	protected function register_avatar_drivers($avatar_drivers) +	{ +		if (!empty($avatar_drivers)) +		{ +			foreach ($avatar_drivers as $driver) +			{ +				$this->avatar_drivers[$driver->get_name()] = $driver; +			} +		} +	} + +	/** +	* Get the driver object specified by the avatar type +	* +	* @param string $avatar_type Avatar type; by default an avatar's service container name +	* @param bool $load_enabled Load only enabled avatars +	* +	* @return object Avatar driver object +	*/ +	public function get_driver($avatar_type, $load_enabled = true) +	{ +		if (self::$enabled_drivers === false) +		{ +			$this->load_enabled_drivers(); +		} + +		$avatar_drivers = ($load_enabled) ? self::$enabled_drivers : $this->get_all_drivers(); + +		// Legacy stuff... +		switch ($avatar_type) +		{ +			case AVATAR_GALLERY: +				$avatar_type = 'avatar.driver.local'; +			break; +			case AVATAR_UPLOAD: +				$avatar_type = 'avatar.driver.upload'; +			break; +			case AVATAR_REMOTE: +				$avatar_type = 'avatar.driver.remote'; +			break; +		} + +		if (!isset($avatar_drivers[$avatar_type])) +		{ +			return null; +		} + +		/* +		* There is no need to handle invalid avatar types as the following code +		* will cause a ServiceNotFoundException if the type does not exist +		*/ +		$driver = $this->avatar_drivers[$avatar_type]; + +		return $driver; +	} + +	/** +	* Load the list of enabled drivers +	* This is executed once and fills self::$enabled_drivers +	*/ +	protected function load_enabled_drivers() +	{ +		if (!empty($this->avatar_drivers)) +		{ +			self::$enabled_drivers = array(); +			foreach ($this->avatar_drivers as $driver) +			{ +				if ($this->is_enabled($driver)) +				{ +					self::$enabled_drivers[$driver->get_name()] = $driver->get_name(); +				} +			} +			asort(self::$enabled_drivers); +		} +	} + +	/** +	* Get a list of all avatar drivers +	* +	* As this function will only be called in the ACP avatar settings page, it +	* doesn't make much sense to cache the list of all avatar drivers like the +	* list of the enabled drivers. +	* +	* @return array Array containing a list of all avatar drivers +	*/ +	public function get_all_drivers() +	{ +		$drivers = array(); + +		if (!empty($this->avatar_drivers)) +		{ +			foreach ($this->avatar_drivers as $driver) +			{ +				$drivers[$driver->get_name()] = $driver->get_name(); +			} +			asort($drivers); +		} + +		return $drivers; +	} + +	/** +	* Get a list of enabled avatar drivers +	* +	* @return array Array containing a list of the enabled avatar drivers +	*/ +	public function get_enabled_drivers() +	{ +		if (self::$enabled_drivers === false) +		{ +			$this->load_enabled_drivers(); +		} + +		return self::$enabled_drivers; +	} + +	/** +	* Strip out user_, group_, or other prefixes from array keys +	* +	* @param array	$row			User data or group data +	* @param string $prefix			Prefix of data keys (e.g. user), should not include the trailing underscore +	* +	* @return array	User or group data with keys that have been +	*			stripped from the preceding "user_" or "group_" +	*			Also the group id is prefixed with g, when the prefix group is removed. +	*/ +	static public function clean_row($row, $prefix = '') +	{ +		// Upon creation of a user/group $row might be empty +		if (empty($row)) +		{ +			return self::$default_row; +		} + +		$output = array(); +		foreach ($row as $key => $value) +		{ +			$key = preg_replace("#^(?:{$prefix}_)#", '', $key); +			$output[$key] = $value; +		} + +		if ($prefix === 'group' && isset($output['id'])) +		{ +			$output['id'] = 'g' . $output['id']; +		} + +		return $output; +	} + +	/** +	* Clean driver names that are returned from template files +	* Underscores are replaced with dots +	* +	* @param string $name Driver name +	* +	* @return string Cleaned driver name +	*/ +	static public function clean_driver_name($name) +	{ +		return str_replace(array('\\', '_'), '.', $name); +	} + +	/** +	* Prepare driver names for use in template files +	* Dots are replaced with underscores +	* +	* @param string $name Clean driver name +	* +	* @return string Prepared driver name +	*/ +	static public function prepare_driver_name($name) +	{ +		return str_replace('.', '_', $name); +	} + +	/** +	* Check if avatar is enabled +	* +	* @param object $driver Avatar driver object +	* +	* @return bool True if avatar is enabled, false if it's disabled +	*/ +	public function is_enabled($driver) +	{ +		$config_name = $this->get_driver_config_name($driver); + +		return $this->config["allow_avatar_{$config_name}"]; +	} + +	/** +	* Get the settings array for enabling/disabling an avatar driver +	* +	* @param object $driver Avatar driver object +	* +	* @return array Array of configuration options as consumed by acp_board +	*/ +	public function get_avatar_settings($driver) +	{ +		$config_name = $this->get_driver_config_name($driver); + +		return array( +			'allow_avatar_' . $config_name	=> array('lang' => 'ALLOW_' . strtoupper(str_replace('\\', '_', $config_name)),		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => false), +		); +	} + +	/** +	* Get the config name of an avatar driver +	* +	* @param object $driver Avatar driver object +	* +	* @return string Avatar driver config name +	*/ +	public function get_driver_config_name($driver) +	{ +		return preg_replace('#^phpbb\\\\avatar\\\\driver\\\\#', '', get_class($driver)); +	} + +	/** +	* Replace "error" strings with their real, localized form +	* +	* @param \phpbb\user phpBB User object +	* @param array	$error Array containing error strings +	*        Key values can either be a string with a language key or an array +	*        that will be passed to vsprintf() with the language key in the +	*        first array key. +	* +	* @return array Array containing the localized error strings +	*/ +	public function localize_errors(\phpbb\user $user, $error) +	{ +		foreach ($error as $key => $lang) +		{ +			if (is_array($lang)) +			{ +				$lang_key = array_shift($lang); +				$error[$key] = vsprintf($user->lang($lang_key), $lang); +			} +			else +			{ +				$error[$key] = $user->lang("$lang"); +			} +		} + +		return $error; +	} + +	/** +	* Handle deleting avatars +	* +	* @param \phpbb\db\driver\driver_interface $db phpBB dbal +	* @param \phpbb\user    $user phpBB user object +	* @param array          $avatar_data Cleaned user data containing the user's +	*                               avatar data +	* @param string         $table Database table from which the avatar should be deleted +	* @param string         $prefix Prefix of user data columns in database +	* @return null +	*/ +	public function handle_avatar_delete(\phpbb\db\driver\driver_interface $db, \phpbb\user $user, $avatar_data, $table, $prefix) +	{ +		if ($driver = $this->get_driver($avatar_data['avatar_type'])) +		{ +			$driver->delete($avatar_data); +		} + +		$result = $this->prefix_avatar_columns($prefix, self::$default_row); + +		$sql = 'UPDATE ' . $table . ' +			SET ' . $db->sql_build_array('UPDATE', $result) . ' +			WHERE ' . $prefix . 'id = ' . (int) $avatar_data['id']; +		$db->sql_query($sql); + +		// Make sure we also delete this avatar from the users +		if ($prefix === 'group_') +		{ +			$result = $this->prefix_avatar_columns('user_', self::$default_row); + +			$sql = 'UPDATE ' . USERS_TABLE . ' +				SET ' . $db->sql_build_array('UPDATE', $result) . " +				WHERE user_avatar = '" . $db->sql_escape($avatar_data['avatar']) . "'"; +			$db->sql_query($sql); +		} +	} + +	/** +	 * Prefix avatar columns +	 * +	 * @param string $prefix Column prefix +	 * @param array $data Column data +	 * +	 * @return array Column data with prefixed column names +	 */ +	public function prefix_avatar_columns($prefix, $data) +	{ +		foreach ($data as $key => $value) +		{ +			$data[$prefix . $key] = $value; +			unset($data[$key]); +		} + +		return $data; +	} +}  | 
