1<?php
2/* Copyright (C) 2004-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2016 Laurent Destailleur  <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2016 Regis Houssin        <regis.houssin@inodbox.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 * or see https://www.gnu.org/
19 */
20
21/**
22 *		\file       htdocs/viewimage.php
23 *		\brief      Wrapper to show images into Dolibarr screens.
24 *		\remarks    Call to wrapper is :
25 *					DOL_URL_ROOT.'/viewimage.php?modulepart=diroffile&file=relativepathofofile&cache=0
26 *					DOL_URL_ROOT.'/viewimage.php?hashp=sharekey
27 */
28
29//if (! defined('NOREQUIREUSER'))	define('NOREQUIREUSER','1');	// Not disabled cause need to load personalized language
30//if (! defined('NOREQUIREDB'))		define('NOREQUIREDB','1');		// Not disabled cause need to load personalized language
31if (!defined('NOREQUIRESOC')) {
32	define('NOREQUIRESOC', '1');
33}
34if (!defined('NOREQUIRETRAN')) {
35	define('NOREQUIRETRAN', '1');
36}
37if (!defined('NOCSRFCHECK')) {
38	define('NOCSRFCHECK', '1');
39}
40if (!defined('NOTOKENRENEWAL')) {
41	define('NOTOKENRENEWAL', '1');
42}
43if (!defined('NOREQUIREMENU')) {
44	define('NOREQUIREMENU', '1');
45}
46if (!defined('NOREQUIREHTML')) {
47	define('NOREQUIREHTML', '1');
48}
49if (!defined('NOREQUIREAJAX')) {
50	define('NOREQUIREAJAX', '1');
51}
52
53// Some value of modulepart can be used to get resources that are public so no login are required.
54// Note that only directory logo is free to access without login.
55if (isset($_GET["modulepart"]) && $_GET["modulepart"] == 'mycompany' && preg_match('/^\/?logos\//', $_GET['file'])) {
56	if (!defined("NOLOGIN")) {
57		define("NOLOGIN", 1);
58	}
59	if (!defined("NOCSRFCHECK")) {
60		define("NOCSRFCHECK", 1); // We accept to go on this page from external web site.
61	}
62	if (!defined("NOIPCHECK")) {
63		define("NOIPCHECK", 1); // Do not check IP defined into conf $dolibarr_main_restrict_ip
64	}
65}
66// For direct external download link, we don't need to load/check we are into a login session
67if (isset($_GET["hashp"]) && !defined("NOLOGIN")) {
68	if (!defined("NOLOGIN")) {
69		define("NOLOGIN", 1);
70	}
71	if (!defined("NOCSRFCHECK")) {
72		define("NOCSRFCHECK", 1); // We accept to go on this page from external web site.
73	}
74	if (!defined("NOIPCHECK")) {
75		define("NOIPCHECK", 1); // Do not check IP defined into conf $dolibarr_main_restrict_ip
76	}
77}
78// Some value of modulepart can be used to get resources that are public so no login are required.
79if (isset($_GET["modulepart"]) && $_GET["modulepart"] == 'medias') {
80	if (!defined("NOLOGIN")) {
81		define("NOLOGIN", 1);
82	}
83	if (!defined("NOCSRFCHECK")) {
84		define("NOCSRFCHECK", 1); // We accept to go on this page from external web site.
85	}
86	if (!defined("NOIPCHECK")) {
87		define("NOIPCHECK", 1); // Do not check IP defined into conf $dolibarr_main_restrict_ip
88	}
89}
90
91// Used by TakePOS Auto Order
92if (isset($_GET["modulepart"]) && $_GET["modulepart"] == 'product' && isset($_GET["publictakepos"])) {
93	if (!defined("NOLOGIN")) {
94		define("NOLOGIN", 1);
95	}
96	if (!defined("NOCSRFCHECK")) {
97		define("NOCSRFCHECK", 1); // We accept to go on this page from external web site.
98	}
99	if (!defined("NOIPCHECK")) {
100		define("NOIPCHECK", 1); // Do not check IP defined into conf $dolibarr_main_restrict_ip
101	}
102}
103
104// For multicompany
105$entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1));
106if (is_numeric($entity)) {
107	define("DOLENTITY", $entity);
108}
109
110/**
111 * Header empty
112 *
113 * @ignore
114 * @return	void
115 */
116function llxHeader()
117{
118}
119/**
120 * Footer empty
121 *
122 * @ignore
123 * @return	void
124 */
125function llxFooter()
126{
127}
128
129require 'main.inc.php'; // Load $user and permissions
130require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
131
132$action = GETPOST('action', 'aZ09');
133$original_file = GETPOST('file', 'alphanohtml'); // Do not use urldecode here ($_GET are already decoded by PHP).
134$hashp = GETPOST('hashp', 'aZ09');
135$modulepart = GETPOST('modulepart', 'alpha');
136$urlsource = GETPOST('urlsource', 'alpha');
137$entity = GETPOST('entity', 'int') ?GETPOST('entity', 'int') : $conf->entity;
138
139// Security check
140if (empty($modulepart) && empty($hashp)) {
141	accessforbidden('Bad link. Bad value for parameter modulepart', 0, 0, 1);
142}
143if (empty($original_file) && empty($hashp) && $modulepart != 'barcode') {
144	accessforbidden('Bad link. Missing identification to find file (param file or hashp)', 0, 0, 1);
145}
146if ($modulepart == 'fckeditor') {
147	$modulepart = 'medias'; // For backward compatibility
148}
149
150
151
152/*
153 * Actions
154 */
155
156// None
157
158
159
160/*
161 * View
162 */
163
164if (GETPOST("cache", 'alpha')) {
165	// Important: Following code is to avoid page request by browser and PHP CPU at
166	// each Dolibarr page access.
167	if (empty($dolibarr_nocache)) {
168		header('Cache-Control: max-age=3600, public, must-revalidate');
169		header('Pragma: cache'); // This is to avoid having Pragma: no-cache
170	} else {
171		header('Cache-Control: no-cache');
172	}
173	//print $dolibarr_nocache; exit;
174}
175
176// If we have a hash public (hashp), we guess the original_file.
177if (!empty($hashp)) {
178	include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
179	$ecmfile = new EcmFiles($db);
180	$result = $ecmfile->fetch(0, '', '', '', $hashp);
181	if ($result > 0) {
182		$tmp = explode('/', $ecmfile->filepath, 2); // $ecmfile->filepath is relative to document directory
183		// filepath can be 'users/X' or 'X/propale/PR11111'
184		if (is_numeric($tmp[0])) { // If first tmp is numeric, it is subdir of company for multicompany, we take next part.
185			$tmp = explode('/', $tmp[1], 2);
186		}
187		$moduleparttocheck = $tmp[0]; // moduleparttocheck is first part of path
188
189		if ($modulepart) {	// Not required, so often not defined, for link using public hashp parameter.
190			if ($moduleparttocheck == $modulepart) {
191				// We remove first level of directory
192				$original_file = (($tmp[1] ? $tmp[1].'/' : '').$ecmfile->filename); // this is relative to module dir
193				//var_dump($original_file); exit;
194			} else {
195				accessforbidden('Bad link. File is from another module part.', 0, 0, 1);
196			}
197		} else {
198			$modulepart = $moduleparttocheck;
199			$original_file = (($tmp[1] ? $tmp[1].'/' : '').$ecmfile->filename); // this is relative to module dir
200		}
201	} else {
202		$langs->load("errors");
203		accessforbidden($langs->trans("ErrorFileNotFoundWithSharedLink"), 0, 0, 1);
204	}
205}
206
207// Define mime type
208$type = 'application/octet-stream';
209if (GETPOST('type', 'alpha')) {
210	$type = GETPOST('type', 'alpha');
211} else {
212	$type = dol_mimetype($original_file);
213}
214
215// Security: This wrapper is for images. We do not allow type/html
216if (preg_match('/html/i', $type)) {
217	accessforbidden('Error: Using the image wrapper to output a file with a mime type HTML is not possible.', 0, 0, 1);
218}
219// Security: This wrapper is for images. We do not allow files ending with .noexe
220if (preg_match('/\.noexe$/i', $original_file)) {
221	accessforbidden('Error: Using the image wrapper to output a file ending with .noexe is not allowed.', 0, 0, 1);
222}
223
224// Security: Delete string ../ or ..\ into $original_file
225$original_file = str_replace('../', '/', $original_file);
226$original_file = str_replace('..\\', '/', $original_file);
227
228// Find the subdirectory name as the reference
229$refname = basename(dirname($original_file)."/");
230
231// Security check
232if (empty($modulepart)) {
233	accessforbidden('Bad value for parameter modulepart', 0, 0, 1);
234}
235
236$check_access = dol_check_secure_access_document($modulepart, $original_file, $entity, $refname);
237$accessallowed              = $check_access['accessallowed'];
238$sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals'];
239$fullpath_original_file     = $check_access['original_file']; // $fullpath_original_file is now a full path name
240
241if (!empty($hashp)) {
242	$accessallowed = 1; // When using hashp, link is public so we force $accessallowed
243	$sqlprotectagainstexternals = '';
244} elseif (isset($_GET["publictakepos"])) {
245	if (!empty($conf->global->TAKEPOS_AUTO_ORDER)) {
246		$accessallowed = 1; // Only if TakePOS Public Auto Order is enabled and received publictakepos variable
247	}
248} else {
249	// Basic protection (against external users only)
250	if ($user->socid > 0) {
251		if ($sqlprotectagainstexternals) {
252			$resql = $db->query($sqlprotectagainstexternals);
253			if ($resql) {
254				$num = $db->num_rows($resql);
255				$i = 0;
256				while ($i < $num) {
257					$obj = $db->fetch_object($resql);
258					if ($user->socid != $obj->fk_soc) {
259						$accessallowed = 0;
260						break;
261					}
262					$i++;
263				}
264			}
265		}
266	}
267}
268
269// Security:
270// Limit access if permissions are wrong
271if (!$accessallowed) {
272	accessforbidden();
273}
274
275// Security:
276// On interdit les remontees de repertoire ainsi que les pipe dans les noms de fichiers.
277if (preg_match('/\.\./', $fullpath_original_file) || preg_match('/[<>|]/', $fullpath_original_file)) {
278	dol_syslog("Refused to deliver file ".$fullpath_original_file);
279	print "ErrorFileNameInvalid: ".dol_escape_htmltag($original_file);
280	exit;
281}
282
283
284
285if ($modulepart == 'barcode') {
286	$generator = GETPOST("generator", "aZ09");
287	$encoding = GETPOST("encoding", "aZ09");
288	$readable = GETPOST("readable", 'aZ09') ? GETPOST("readable", "aZ09") : "Y";
289	if (in_array($encoding, array('EAN8', 'EAN13'))) {
290		$code = GETPOST("code", 'alphanohtml');
291	} else {
292		$code = GETPOST("code", 'none'); // This can be rich content (qrcode, datamatrix, ...)
293	}
294
295	if (empty($generator) || empty($encoding)) {
296		print 'Error: Parameter "generator" or "encoding" not defined';
297		exit;
298	}
299
300	$dirbarcode = array_merge(array("/core/modules/barcode/doc/"), $conf->modules_parts['barcode']);
301
302	$result = 0;
303
304	foreach ($dirbarcode as $reldir) {
305		$dir = dol_buildpath($reldir, 0);
306		$newdir = dol_osencode($dir);
307
308		// Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php)
309		if (!is_dir($newdir)) {
310			continue;
311		}
312
313		$result = @include_once $newdir.$generator.'.modules.php';
314		if ($result) {
315			break;
316		}
317	}
318
319	// Load barcode class
320	$classname = "mod".ucfirst($generator);
321	$module = new $classname($db);
322	if ($module->encodingIsSupported($encoding)) {
323		$result = $module->buildBarCode($code, $encoding, $readable);
324	}
325} else {
326	// Open and return file
327	clearstatcache();
328
329	$filename = basename($fullpath_original_file);
330
331	// Output files on browser
332	dol_syslog("viewimage.php return file $fullpath_original_file filename=$filename content-type=$type");
333
334	// This test is to avoid error images when image is not available (for example thumbs).
335	if (!dol_is_file($fullpath_original_file) && empty($_GET["noalt"])) {
336		$fullpath_original_file = DOL_DOCUMENT_ROOT.'/public/theme/common/nophoto.png';
337		/*$error='Error: File '.$_GET["file"].' does not exists or filesystems permissions are not allowed';
338		print $error;
339		exit;*/
340	}
341
342	// Permissions are ok and file found, so we return it
343	if ($type) {
344		top_httphead($type);
345		header('Content-Disposition: inline; filename="'.basename($fullpath_original_file).'"');
346	} else {
347		top_httphead('image/png');
348		header('Content-Disposition: inline; filename="'.basename($fullpath_original_file).'"');
349	}
350
351	$fullpath_original_file_osencoded = dol_osencode($fullpath_original_file);
352
353	readfile($fullpath_original_file_osencoded);
354}
355
356
357if (is_object($db)) {
358	$db->close();
359}
360