HEX
Server: Apache
System: Linux darrell.nocdirect.com 4.18.0-513.18.2.el8_9.x86_64 #1 SMP Sat Mar 30 06:10:41 EDT 2024 x86_64
User: joderbya (1358)
PHP: 8.0.30
Disabled: NONE
Upload Files
File: /home/joderbya/public_html/ss-servicos/nacala/webportal/controllers/viewimage.controller.class.php
<?php
/*
 * Copyright (C) 2024       Frédéric France             <frederic.france@free.fr>
 * Copyright (C) 2024		MDW							<mdeweerd@users.noreply.github.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

/**
* \file        htdocs/webportal/controllers/viewimage.controller.class.php
* \ingroup     webportal
* \brief       This file is a controller for documents
*/

require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';


/**
 * Class for ViewImageController
 */
class ViewImageController extends Controller
{
	/**
	 * @var string Action
	 */
	public $action;

	/**
	 * @var int Entity
	 */
	public $entity;

	/**
	 * @var string File name
	 */
	public $filename;

	/**
	 * @var string Full path of original file
	 */
	public $fullpath_original_file;

	/**
	 * @var string Full path of original file with encoded for OS
	 */
	public $fullpath_original_file_osencoded;

	/**
	 * @var string Module of document ('module', 'module_user_temp', 'module_user' or 'module_temp'). Example: 'medias', 'invoice', 'logs', 'tax-vat', ...
	 */
	public $modulepart;

	/**
	 * @var string Relative path with filename, relative to modulepart.
	 */
	public $original_file;

	/**
	 * @var string Mime type of file
	 */
	public $type;


	/**
	 * Init
	 *
	 * @return	void
	 */
	public function init()
	{
		global $conf, $hookmanager, $dolibarr_nocache, $user;

		define('MAIN_SECURITY_FORCECSP', "default-src 'none'");

		if (!defined('NOREQUIRESOC')) {
			define('NOREQUIRESOC', '1');
		}
		if (!defined('NOREQUIRETRAN')) {
			define('NOREQUIRETRAN', '1');
		}
		if (!defined('NOCSRFCHECK')) {
			define('NOCSRFCHECK', '1');
		}
		if (!defined('NOTOKENRENEWAL')) {
			define('NOTOKENRENEWAL', '1');
		}
		if (!defined('NOREQUIREMENU')) {
			define('NOREQUIREMENU', '1');
		}
		if (!defined('NOREQUIREHTML')) {
			define('NOREQUIREHTML', '1');
		}
		if (!defined('NOREQUIREAJAX')) {
			define('NOREQUIREAJAX', '1');
		}

		// For MultiCompany module.
		// Do not use GETPOST here, function is not defined and define must be done before including main.inc.php
		// Because 2 entities can have the same ref.
		$entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1));
		define("DOLENTITY", $entity);

		$context = Context::getInstance();

		$action = GETPOST('action', 'aZ09');
		$original_file = GETPOST('file', 'alphanohtml');
		$hashp = GETPOST('hashp', 'aZ09', 1);
		$extname = GETPOST('extname', 'alpha', 1);
		$modulepart = GETPOST('modulepart', 'alpha', 1);
		$urlsource = GETPOST('urlsource', 'alpha');
		$entity = (GETPOSTINT('entity') ? GETPOSTINT('entity') : $conf->entity);

		// Security check
		if (empty($modulepart) && empty($hashp)) {
			httponly_accessforbidden('Bad link. Bad value for parameter modulepart', 400);
		}
		if (empty($original_file) && empty($hashp) && $modulepart != 'barcode') {
			httponly_accessforbidden('Bad link. Missing identification to find file (param file or hashp)', 400);
		}
		if ($modulepart == 'fckeditor') {
			$modulepart = 'medias'; // For backward compatibility
		}

		$cachestring = GETPOST("cache", 'aZ09');    // May be 1, or an int, or a hash
		if ($cachestring) {
			// Important: The following code is to avoid a page request by the browser and PHP CPU at each Dolibarr page access.
			// We are here when param cache=xxx to force a cache policy:
			//  xxx=1 means cache of 3600s
			//  xxx=abcdef or 123456789 means a cache of 1 week (the key will be modified to get break cache use)
			if (empty($dolibarr_nocache)) {
				$delaycache = ((is_numeric($cachestring) && (int) $cachestring > 1 && (int) $cachestring < 999999) ? $cachestring : '3600');
				header('Cache-Control: max-age=' . $delaycache . ', public, must-revalidate');
				header('Pragma: cache'); // This is to avoid to have Pragma: no-cache set by proxy or web server
				header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (int) $delaycache) . ' GMT');    // This is to avoid to have Expires set by proxy or web server
			} else {
				// If any cache on files were disable by config file (for test purpose)
				header('Cache-Control: no-cache');
			}
			//print $dolibarr_nocache; exit;
		}

		// Define mime type
		$type = 'application/octet-stream';
		if (GETPOST('type', 'alpha')) {
			$type = GETPOST('type', 'alpha');
		} else {
			$type = dol_mimetype($original_file);
		}

		// Security: This wrapper is for images. We do not allow type/html
		if (preg_match('/html/i', $type)) {
			httponly_accessforbidden('Error: Using the image wrapper to output a file with a mime type HTML is not possible.');
		}
		// Security: This wrapper is for images. We do not allow files ending with .noexe
		if (preg_match('/\.noexe$/i', $original_file)) {
			httponly_accessforbidden('Error: Using the image wrapper to output a file ending with .noexe is not allowed.');
		}

		// Security: Delete string ../ or ..\ into $original_file
		$original_file = preg_replace('/\.\.+/', '..', $original_file);    // Replace '... or more' with '..'
		$original_file = str_replace('../', '/', $original_file);
		$original_file = str_replace('..\\', '/', $original_file);

		// Find the subdirectory name as the reference
		$refname = basename(dirname($original_file) . "/");
		if ($refname == 'thumbs') {
			// If we get the thumbs directory, we must go one step higher. For example original_file='10/thumbs/myfile_small.jpg' -> refname='10'
			$refname = basename(dirname(dirname($original_file)) . "/");
		}

		// Check that file is allowed for view with viewimage.php
		if (!empty($original_file) && !dolIsAllowedForPreview($original_file)) {
			httponly_accessforbidden('This file extension is not qualified for preview', 403);
		}

		// Security check
		if (empty($modulepart)) {
			httponly_accessforbidden('Bad value for parameter modulepart', 400);
		}

		// When logged in a different entity, medias cannot be accessed because $conf->$module->multidir_output
		// is not set on the requested entity, but they are public documents, so reset entity
		if ($modulepart === 'medias' && $entity != $conf->entity) {
			$conf->entity = $entity;
			$conf->setValues($this->db);
		}

		$sqlprotectagainstexternals = '';
		$fullpath_original_file = '';
		$accessallowed = 0;

		// Hooks
		$hookmanager->initHooks(array('viewimage'));
		$parameters = array('modulepart' => $modulepart, 'original_file' => &$original_file,
			'sqlprotectagainstexternals' => &$sqlprotectagainstexternals, 'fullpath_original_file' => &$fullpath_original_file,
			'entity' => $entity, 'accessallowed' => &$accessallowed);
		$object = new stdClass();
		$reshook = $hookmanager->executeHooks('accessViewImage', $parameters, $object, $action); // Note that $action and $object may have been
		if ($reshook < 0) {
			$errors = $hookmanager->error . (is_array($hookmanager->errors) ? (!empty($hookmanager->error) ? ', ' : '') . implode(', ', $hookmanager->errors) : '');
			dol_syslog("document.php - Errors when executing the hook 'accessViewImage' : " . $errors);
			print "ErrorViewImageHooks: " . $errors;
			exit;
		} elseif (empty($reshook)) {
			$check_access = dol_check_secure_access_document($modulepart, $original_file, $entity, $user, $refname);
			$accessallowed = $check_access['accessallowed'];
			$sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals'];
			$fullpath_original_file = $check_access['original_file']; // $fullpath_original_file is now a full path name
		}

		if (!empty($hashp)) {
			$accessallowed = 1; // When using hashp, link is public so we force $accessallowed
			$sqlprotectagainstexternals = '';
		} else {
			// Basic protection (against external users only)
			if ($user->socid > 0) {
				if ($sqlprotectagainstexternals) {
					$resql = $this->db->query($sqlprotectagainstexternals);
					if ($resql) {
						$num = $this->db->num_rows($resql);
						$i = 0;
						while ($i < $num) {
							$obj = $this->db->fetch_object($resql);
							if ($user->socid != $obj->fk_soc) {
								$accessallowed = 0;
								break;
							}
							$i++;
						}
					}
				}
			}
		}

		// Security:
		// Limit access if permissions are wrong
		if (!$accessallowed) {
			accessforbidden();
		}

		// Security:
		// On interdit les remontees de repertoire ainsi que les pipe dans les noms de fichiers.
		if (preg_match('/\.\./', $fullpath_original_file) || preg_match('/[<>|]/', $fullpath_original_file)) {
			dol_syslog("Refused to deliver file " . $fullpath_original_file);
			print "ErrorFileNameInvalid: " . dol_escape_htmltag($original_file);
			exit;
		}

		// Find the subdirectory name as the reference
		$refname = basename(dirname($original_file) . "/");

		$filename = basename($fullpath_original_file);
		$filename = preg_replace('/\.noexe$/i', '', $filename);

		// Output file on browser
		dol_syslog("document controller download $fullpath_original_file filename=$filename content-type=$type");
		$fullpath_original_file_osencoded = dol_osencode($fullpath_original_file); // New file name encoded in OS encoding charset

		// This test if file exists should be useless. We keep it to find bug more easily
		if (!file_exists($fullpath_original_file_osencoded)) {
			dol_syslog("ErrorFileDoesNotExists: " . $fullpath_original_file);
			print "ErrorFileDoesNotExists: " . $original_file;
			exit;
		}

		$fileSize = dol_filesize($fullpath_original_file);
		$fileSizeMax = getDolGlobalInt('MAIN_SECURITY_MAXFILESIZE_DOWNLOADED');
		if ($fileSizeMax && $fileSize > $fileSizeMax) {
			dol_syslog('ErrorFileSizeTooLarge: ' . $fileSize);
			print 'ErrorFileSizeTooLarge: ' . $fileSize . ' (max ' . $fileSizeMax . ' Kb)';
			exit;
		}

		// Hooks
		$hookmanager->initHooks(array('document'));
		$parameters = array('modulepart' => $modulepart, 'original_file' => $original_file,
			'entity' => $entity, 'refname' => $refname, 'fullpath_original_file' => $fullpath_original_file,
			'filename' => $filename, 'fullpath_original_file_osencoded' => $fullpath_original_file_osencoded);
		$object = new stdClass();
		$reshook = $hookmanager->executeHooks('viewImage', $parameters, $object, $action); // Note that $action and $object may have been
		if ($reshook < 0) {
			$errors = $hookmanager->error . (is_array($hookmanager->errors) ? (!empty($hookmanager->error) ? ', ' : '') . implode(', ', $hookmanager->errors) : '');
			dol_syslog("document.php - Errors when executing the hook 'viewImage' : " . $errors);
			print "ErrorViewImageHooks: " . $errors;
			exit;
		}

		$this->action = $action;
		$this->entity = $entity;
		$this->filename = $filename;
		$this->fullpath_original_file = $fullpath_original_file;
		$this->fullpath_original_file_osencoded = $fullpath_original_file_osencoded;
		$this->modulepart = $modulepart;
		$this->original_file = $original_file;
		$this->type = $type;
	}

	/**
	 * Check current access to controller
	 *
	 * @return  bool
	 */
	public function checkAccess()
	{
		$this->accessRight = true;

		return parent::checkAccess();
	}

	/**
	 * Action method is called before html output
	 * can be used to manage security and change context
	 *
	 * @return  int     Return integer < 0 on error, > 0 on success
	 */
	public function action()
	{
		$context = Context::getInstance();
		if (!$context->controllerInstance->checkAccess()) {
			return -1;
		}

		//$context = Context::getInstance();
		//$context->title = $langs->trans('WebPortalDocumentTitle');
		//$context->desc = $langs->trans('WebPortalDocumentDesc');
		//$context->doNotDisplayHeaderBar=1;// hide default header

		$this->init();

		return 1;
	}

	/**
	 * Display
	 *
	 * @return  void
	 */
	public function display()
	{
		global $conf;

		$context = Context::getInstance();
		if (!$context->controllerInstance->checkAccess()) {
			$this->display404();
			return;
		}

		// initialize
		$modulepart = $this->modulepart;
		$fullpath_original_file = $this->fullpath_original_file;
		$fullpath_original_file_osencoded = $this->fullpath_original_file_osencoded;
		$type = $this->type;

		if ($modulepart == 'barcode') {
			$generator = GETPOST("generator", "aZ09");
			$encoding = GETPOST("encoding", "aZ09");
			$readable = GETPOST("readable", 'aZ09') ? GETPOST("readable", "aZ09") : "Y";
			if (in_array($encoding, array('EAN8', 'EAN13'))) {
				$code = GETPOST("code", 'alphanohtml');
			} else {
				$code = GETPOST("code", 'restricthtml'); // This can be rich content (qrcode, datamatrix, ...)
			}

			// If $code is virtualcard_xxx_999.vcf, it is a file to read to get code
			$reg = array();
			if (preg_match('/^virtualcard_([^_]+)_(\d+)\.vcf$/', $code, $reg)) {
				$vcffile = '';
				$id = 0;
				$login = '';
				if ($reg[1] == 'user' && (int) $reg[2] > 0) {
					$vcffile = $conf->user->dir_temp . '/' . $code;
					$id = (int) $reg[2];
					$tmpuser = new User($this->db);
					$tmpuser->fetch($id);
					$login = $tmpuser->login;
				} elseif ($reg[1] == 'contact' && (int) $reg[2] > 0) {
					$vcffile = $conf->contact->dir_temp . '/' . $code;
					$id = (int) $reg[2];
				}

				$code = '';
				if ($vcffile && $id) {
					// Case of use of viewimage to get the barcode for user pubic profile,
					// we must check the securekey that protet against forging url
					if ($reg[1] == 'user' && (int) $reg[2] > 0) {
						$encodedsecurekey = dol_hash($conf->file->instance_unique_id . 'uservirtualcard' . $id . '-' . $login, 'md5');
						if ($encodedsecurekey != GETPOST('securekey')) {
							$code = 'badvalueforsecurekey';
						}
					}
					if (empty($code)) {
						$code = file_get_contents($vcffile);
					}
				}
			}


			if (empty($generator) || empty($encoding)) {
				print 'Error: Parameter "generator" or "encoding" not defined';
				exit;
			}

			$dirbarcode = array_merge(array("/core/modules/barcode/doc/"), $conf->modules_parts['barcode']);

			$result = 0;

			foreach ($dirbarcode as $reldir) {
				$dir = dol_buildpath($reldir, 0);
				$newdir = dol_osencode($dir);

				// Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php)
				if (!is_dir($newdir)) {
					continue;
				}

				$result = @include_once $newdir . $generator . '.modules.php';
				if ($result) {
					break;
				}
			}

			// Load barcode class
			$classname = "mod" . ucfirst($generator);

			$module = new $classname($this->db);
			'@phan-var-force ModeleBarCode $module';
			/** @var ModeleBarCode $module */
			if ($module->encodingIsSupported($encoding)) {
				$result = $module->buildBarCode($code, $encoding, $readable);
			}
		} else {
			// Open and return file
			clearstatcache();

			$filename = basename($fullpath_original_file);

			// Output files on browser
			dol_syslog("viewimage.php return file $fullpath_original_file filename=$filename content-type=$type");

			if (!dol_is_file($fullpath_original_file) && !GETPOSTINT("noalt", 1)) {
				// This test is to replace error images with a nice "notfound image" when image is not available (for example when thumbs not yet generated).
				$fullpath_original_file = Context::getRootConfigUrl() . 'img/nophoto.png';
				/*$error='Error: File '.$_GET["file"].' does not exists or filesystems permissions are not allowed';
				print $error;
				exit;*/
			}

			// Permissions are ok and file found, so we return it
			if ($type) {
				top_httphead($type);
				header('Content-Disposition: inline; filename="' . basename($fullpath_original_file) . '"');
			} else {
				top_httphead('image/png');
				header('Content-Disposition: inline; filename="' . basename($fullpath_original_file) . '"');
			}

			$fullpath_original_file_osencoded = dol_osencode($fullpath_original_file);

			readfile($fullpath_original_file_osencoded);
		}
	}
}