dolibarr 21.0.3
document.controller.class.php
Go to the documentation of this file.
1<?php
2/*
3 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.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 */
19
26require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
27
28
33{
37 public $action;
38
42 public $attachment;
43
47 public $encoding;
48
52 public $entity;
53
57 public $filename;
58
62 public $fullpath_original_file;
63
67 public $fullpath_original_file_osencoded;
68
72 public $modulepart;
73
77 public $original_file;
78
82 public $type;
83
84
90 public function init()
91 {
92 global $conf, $hookmanager;
93
94 define('MAIN_SECURITY_FORCECSP', "default-src: 'none'");
95
96 if (!defined('NOTOKENRENEWAL')) {
97 define('NOTOKENRENEWAL', '1');
98 }
99 if (!defined('NOREQUIREMENU')) {
100 define('NOREQUIREMENU', '1');
101 }
102 if (!defined('NOREQUIREHTML')) {
103 define('NOREQUIREHTML', '1');
104 }
105 if (!defined('NOREQUIREAJAX')) {
106 define('NOREQUIREAJAX', '1');
107 }
108
110
111 $encoding = '';
112 $action = GETPOST('action', 'aZ09');
113 $original_file = GETPOST('file', 'alphanohtml'); // Do not use urldecode here ($_GET are already decoded by PHP).
114 $modulepart = GETPOST('modulepart', 'alpha');
115 $entity = GETPOSTINT('entity') ? GETPOSTINT('entity') : $conf->entity;
116 $socId = GETPOSTINT('soc_id');
117
118 // Security check
119 if (empty($modulepart)) {
120 httponly_accessforbidden('Bad link. Bad value for parameter modulepart', 400);
121 }
122 if (empty($original_file)) {
123 httponly_accessforbidden('Bad link. Missing identification to find file (original_file)', 400);
124 }
125
126 // get original file
127 $ecmfile = '';
128
129 // Define attachment (attachment=true to force choice popup 'open'/'save as')
130 $attachment = true;
131 if (preg_match('/\.(html|htm)$/i', $original_file)) {
132 $attachment = false;
133 }
134 if (GETPOSTISSET("attachment")) {
135 $attachment = GETPOST("attachment", 'alpha') ? true : false;
136 }
137 if (getDolGlobalString('MAIN_DISABLE_FORCE_SAVEAS')) {
138 $attachment = false;
139 }
140
141 // Define mime type
142 if (GETPOST('type', 'alpha')) {
143 $type = GETPOST('type', 'alpha');
144 } else {
145 $type = dol_mimetype($original_file);
146 }
147 // Security: Force to octet-stream if file is a dangerous file. For example when it is a .noexe file
148 // We do not force if file is a javascript to be able to get js from website module with <script src="
149 // Note: Force whatever is $modulepart seems ok.
150 if (!in_array($type, array('text/x-javascript')) && !dolIsAllowedForPreview($original_file)) {
151 $type = 'application/octet-stream';
152 }
153
154 // Security: Delete string ../ or ..\ into $original_file
155 $original_file = preg_replace('/\.\.+/', '..', $original_file); // Replace '... or more' with '..'
156 $original_file = str_replace('../', '/', $original_file);
157 $original_file = str_replace('..\\', '/', $original_file);
158
159 // Check security and set return info with full path of file
160 $accessallowed = 0; // not allowed by default
161 $moduleName = $modulepart;
162 $moduleNameEn = $moduleName;
163 if ($moduleName == 'commande') {
164 $moduleNameEn = 'order';
165 } elseif ($moduleName == 'facture') {
166 $moduleNameEn = 'invoice';
167 }
168 $moduleNameUpperEn = strtoupper($moduleNameEn);
169 // check config access
170 // and file mime type (only PDF)
171 // and check login access
172 if (getDolGlobalInt('WEBPORTAL_' . $moduleNameUpperEn . '_LIST_ACCESS')
173 && in_array($type, array('application/pdf'))
174 && ($context->logged_thirdparty && $context->logged_thirdparty->id > 0)
175 && $context->logged_thirdparty->id == $socId
176 ) {
177 if (isModEnabled($moduleName) && isset($conf->{$moduleName}->multidir_output[$entity])) {
178 $original_file = $conf->{$moduleName}->multidir_output[$entity] . '/' . $original_file;
179 $accessallowed = 1;
180 }
181 }
182 $fullpath_original_file = $original_file; // $fullpath_original_file is now a full path name
183
184 // Security:
185 // Limit access if permissions are wrong
186 if (!$accessallowed) {
188 }
189
190 // Security:
191 // We refuse directory transversal change and pipes in file names
192 if (preg_match('/\.\./', $fullpath_original_file) || preg_match('/[<>|]/', $fullpath_original_file)) {
193 dol_syslog("Refused to deliver file " . $fullpath_original_file);
194 print "ErrorFileNameInvalid: " . dol_escape_htmltag($original_file);
195 exit;
196 }
197
198 // Find the subdirectory name as the reference
199 $refname = basename(dirname($original_file) . "/");
200
201 $filename = basename($fullpath_original_file);
202 $filename = preg_replace('/\.noexe$/i', '', $filename);
203
204 // Output file on browser
205 dol_syslog("document controller download $fullpath_original_file filename=$filename content-type=$type");
206 $fullpath_original_file_osencoded = dol_osencode($fullpath_original_file); // New file name encoded in OS encoding charset
207
208 // This test if file exists should be useless. We keep it to find bug more easily
209 if (!file_exists($fullpath_original_file_osencoded)) {
210 dol_syslog("ErrorFileDoesNotExists: " . $fullpath_original_file);
211 print "ErrorFileDoesNotExists: " . $original_file;
212 exit;
213 }
214
215 $fileSize = dol_filesize($fullpath_original_file);
216 $fileSizeMax = getDolGlobalInt('MAIN_SECURITY_MAXFILESIZE_DOWNLOADED');
217 if ($fileSizeMax && $fileSize > $fileSizeMax) {
218 dol_syslog('ErrorFileSizeTooLarge: ' . $fileSize);
219 print 'ErrorFileSizeTooLarge: ' . $fileSize . ' (max ' . $fileSizeMax . ')';
220 exit;
221 }
222
223 // Hooks
224 $hookmanager->initHooks(array('document'));
225 $parameters = array('ecmfile' => $ecmfile, 'modulepart' => $modulepart, 'original_file' => $original_file,
226 'entity' => $entity, 'refname' => $refname, 'fullpath_original_file' => $fullpath_original_file,
227 'filename' => $filename, 'fullpath_original_file_osencoded' => $fullpath_original_file_osencoded);
228 $object = new stdClass();
229 $reshook = $hookmanager->executeHooks('downloadDocument', $parameters, $object, $action); // Note that $action and $object may have been
230 if ($reshook < 0) {
231 $errors = $hookmanager->error . (is_array($hookmanager->errors) ? (!empty($hookmanager->error) ? ', ' : '') . implode(', ', $hookmanager->errors) : '');
232 dol_syslog("document.php - Errors when executing the hook 'downloadDocument' : " . $errors);
233 print "ErrorDownloadDocumentHooks: " . $errors;
234 exit;
235 }
236
237 $this->action = $action;
238 $this->attachment = $attachment;
239 $this->encoding = $encoding;
240 $this->entity = $entity;
241 $this->filename = $filename;
242 $this->fullpath_original_file = $fullpath_original_file;
243 $this->fullpath_original_file_osencoded = $fullpath_original_file_osencoded;
244 $this->modulepart = $modulepart;
245 $this->original_file = $original_file;
246 $this->type = $type;
247 }
248
254 public function checkAccess()
255 {
256 $this->accessRight = true;
257
258 return parent::checkAccess();
259 }
260
267 public function action()
268 {
270 if (!$context->controllerInstance->checkAccess()) {
271 return -1;
272 }
273
274 //$context = Context::getInstance();
275 //$context->title = $langs->trans('WebPortalDocumentTitle');
276 //$context->desc = $langs->trans('WebPortalDocumentDesc');
277 //$context->doNotDisplayHeaderBar=1;// hide default header
278
279 $this->init();
280
281 return 1;
282 }
283
289 public function display()
290 {
292 if (!$context->controllerInstance->checkAccess()) {
293 $this->display404();
294 return;
295 }
296
297 // initialize
298 $attachment = $this->attachment;
299 $encoding = $this->encoding;
300 $filename = $this->filename;
301 $fullpath_original_file = $this->fullpath_original_file;
302 $fullpath_original_file_osencoded = $this->fullpath_original_file_osencoded;
303 $type = $this->type;
304
305 clearstatcache();
306
307 // Permissions are ok and file found, so we return it
308 top_httphead($type);
309 header('Content-Description: File Transfer');
310 if ($encoding) {
311 header('Content-Encoding: ' . $encoding);
312 }
313 // Add MIME Content-Disposition from RFC 2183 (inline=automatically displayed, attachment=need user action to open)
314 if ($attachment) {
315 header('Content-Disposition: attachment; filename="' . $filename . '"');
316 } else {
317 header('Content-Disposition: inline; filename="' . $filename . '"');
318 }
319 header('Cache-Control: Public, must-revalidate');
320 header('Pragma: public');
321
322 // Send file now
323 header('Content-Length: ' . dol_filesize($fullpath_original_file));
324 readfileLowMemory($fullpath_original_file_osencoded);
325 }
326}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
static getInstance()
Singleton method to create one instance of this object.
Class to manage pages.
Class for DocumentController.
action()
Action method is called before html output can be used to manage security and change context.
checkAccess()
Check current access to controller.
dol_filesize($pathoffile)
Return size of a file.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
dolIsAllowedForPreview($file)
Return if a file is qualified for preview.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
$context
@method int call_trigger(string $triggerName, User $user)
Definition logout.php:42
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:150
httponly_accessforbidden($message='1', $http_response_code=403, $stringalreadysanitized=0)
Show a message to say access is forbidden and stop program.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.