dolibarr 21.0.0-beta
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 $fileSizeMaxDefault = 20 * 1024; // 20 Mo by default
217 $fileSizeMax = getDolGlobalInt('MAIN_SECURITY_MAXFILESIZE_DOWNLOADED', $fileSizeMaxDefault);
218 if ($fileSize > $fileSizeMax) {
219 dol_syslog('ErrorFileSizeTooLarge: ' . $fileSize);
220 print 'ErrorFileSizeTooLarge: ' . $fileSize . ' (max ' . $fileSizeMax . ')';
221 exit;
222 }
223
224 // Hooks
225 $hookmanager->initHooks(array('document'));
226 $parameters = array('ecmfile' => $ecmfile, 'modulepart' => $modulepart, 'original_file' => $original_file,
227 'entity' => $entity, 'refname' => $refname, 'fullpath_original_file' => $fullpath_original_file,
228 'filename' => $filename, 'fullpath_original_file_osencoded' => $fullpath_original_file_osencoded);
229 $object = new stdClass();
230 $reshook = $hookmanager->executeHooks('downloadDocument', $parameters, $object, $action); // Note that $action and $object may have been
231 if ($reshook < 0) {
232 $errors = $hookmanager->error . (is_array($hookmanager->errors) ? (!empty($hookmanager->error) ? ', ' : '') . implode(', ', $hookmanager->errors) : '');
233 dol_syslog("document.php - Errors when executing the hook 'downloadDocument' : " . $errors);
234 print "ErrorDownloadDocumentHooks: " . $errors;
235 exit;
236 }
237
238 $this->action = $action;
239 $this->attachment = $attachment;
240 $this->encoding = $encoding;
241 $this->entity = $entity;
242 $this->filename = $filename;
243 $this->fullpath_original_file = $fullpath_original_file;
244 $this->fullpath_original_file_osencoded = $fullpath_original_file_osencoded;
245 $this->modulepart = $modulepart;
246 $this->original_file = $original_file;
247 $this->type = $type;
248 }
249
255 public function checkAccess()
256 {
257 $this->accessRight = true;
258
259 return parent::checkAccess();
260 }
261
268 public function action()
269 {
271 if (!$context->controllerInstance->checkAccess()) {
272 return -1;
273 }
274
275 //$context = Context::getInstance();
276 //$context->title = $langs->trans('WebPortalDocumentTitle');
277 //$context->desc = $langs->trans('WebPortalDocumentDesc');
278 //$context->doNotDisplayHeaderBar=1;// hide default header
279
280 $this->init();
281
282 return 1;
283 }
284
290 public function display()
291 {
293 if (!$context->controllerInstance->checkAccess()) {
294 $this->display404();
295 return;
296 }
297
298 // initialize
299 $attachment = $this->attachment;
300 $encoding = $this->encoding;
301 $filename = $this->filename;
302 $fullpath_original_file = $this->fullpath_original_file;
303 $fullpath_original_file_osencoded = $this->fullpath_original_file_osencoded;
304 $type = $this->type;
305
306 clearstatcache();
307
308 // Permissions are ok and file found, so we return it
309 top_httphead($type);
310 header('Content-Description: File Transfer');
311 if ($encoding) {
312 header('Content-Encoding: ' . $encoding);
313 }
314 // Add MIME Content-Disposition from RFC 2183 (inline=automatically displayed, attachment=need user action to open)
315 if ($attachment) {
316 header('Content-Disposition: attachment; filename="' . $filename . '"');
317 } else {
318 header('Content-Disposition: inline; filename="' . $filename . '"');
319 }
320 header('Cache-Control: Public, must-revalidate');
321 header('Pragma: public');
322
323 // Send file now
324 header('Content-Length: ' . dol_filesize($fullpath_original_file));
325 readfileLowMemory($fullpath_original_file_osencoded);
326 }
327}
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:149
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.