dolibarr 20.0.0
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 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
25require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
26
27
32{
36 public $action;
37
41 public $attachment;
42
46 public $encoding;
47
51 public $entity;
52
56 public $filename;
57
61 public $fullpath_original_file;
62
66 public $fullpath_original_file_osencoded;
67
71 public $modulepart;
72
76 public $original_file;
77
81 public $type;
82
83
89 public function init()
90 {
91 global $conf, $hookmanager;
92
93 define('MAIN_SECURITY_FORCECSP', "default-src: 'none'");
94
95 if (!defined('NOTOKENRENEWAL')) {
96 define('NOTOKENRENEWAL', '1');
97 }
98 if (!defined('NOREQUIREMENU')) {
99 define('NOREQUIREMENU', '1');
100 }
101 if (!defined('NOREQUIREHTML')) {
102 define('NOREQUIREHTML', '1');
103 }
104 if (!defined('NOREQUIREAJAX')) {
105 define('NOREQUIREAJAX', '1');
106 }
107
108 $context = Context::getInstance();
109
110 $encoding = '';
111 $action = GETPOST('action', 'aZ09');
112 $original_file = GETPOST('file', 'alphanohtml'); // Do not use urldecode here ($_GET are already decoded by PHP).
113 $modulepart = GETPOST('modulepart', 'alpha');
114 $entity = GETPOSTINT('entity') ? GETPOSTINT('entity') : $conf->entity;
115 $socId = GETPOSTINT('soc_id');
116
117 // Security check
118 if (empty($modulepart)) {
119 httponly_accessforbidden('Bad link. Bad value for parameter modulepart', 400);
120 exit;
121 }
122 if (empty($original_file)) {
123 httponly_accessforbidden('Bad link. Missing identification to find file (original_file)', 400);
124 exit;
125 }
126
127 // get original file
128 $ecmfile = '';
129
130 // Define attachment (attachment=true to force choice popup 'open'/'save as')
131 $attachment = true;
132 if (preg_match('/\.(html|htm)$/i', $original_file)) {
133 $attachment = false;
134 }
135 if (GETPOSTISSET("attachment")) {
136 $attachment = GETPOST("attachment", 'alpha') ? true : false;
137 }
138 if (getDolGlobalString('MAIN_DISABLE_FORCE_SAVEAS')) {
139 $attachment = false;
140 }
141
142 // Define mime type
143 if (GETPOST('type', 'alpha')) {
144 $type = GETPOST('type', 'alpha');
145 } else {
146 $type = dol_mimetype($original_file);
147 }
148 // Security: Force to octet-stream if file is a dangerous file. For example when it is a .noexe file
149 // We do not force if file is a javascript to be able to get js from website module with <script src="
150 // Note: Force whatever is $modulepart seems ok.
151 if (!in_array($type, array('text/x-javascript')) && !dolIsAllowedForPreview($original_file)) {
152 $type = 'application/octet-stream';
153 }
154
155 // Security: Delete string ../ or ..\ into $original_file
156 $original_file = preg_replace('/\.\.+/', '..', $original_file); // Replace '... or more' with '..'
157 $original_file = str_replace('../', '/', $original_file);
158 $original_file = str_replace('..\\', '/', $original_file);
159
160 // Check security and set return info with full path of file
161 $accessallowed = 0; // not allowed by default
162 $moduleName = $modulepart;
163 $moduleNameEn = $moduleName;
164 if ($moduleName == 'commande') {
165 $moduleNameEn = 'order';
166 } elseif ($moduleName == 'facture') {
167 $moduleNameEn = 'invoice';
168 }
169 $moduleNameUpperEn = strtoupper($moduleNameEn);
170 // check config access
171 // and file mime type (only PDF)
172 // and check login access
173 if (getDolGlobalInt('WEBPORTAL_' . $moduleNameUpperEn . '_LIST_ACCESS')
174 && in_array($type, array('application/pdf'))
175 && ($context->logged_thirdparty && $context->logged_thirdparty->id > 0)
176 && $context->logged_thirdparty->id == $socId
177 ) {
178 if (isModEnabled($moduleName) && isset($conf->{$moduleName}->multidir_output[$entity])) {
179 $original_file = $conf->{$moduleName}->multidir_output[$entity] . '/' . $original_file;
180 $accessallowed = 1;
181 }
182 }
183 $fullpath_original_file = $original_file; // $fullpath_original_file is now a full path name
184
185 // Security:
186 // Limit access if permissions are wrong
187 if (!$accessallowed) {
189 }
190
191 // Security:
192 // We refuse directory transversal change and pipes in file names
193 if (preg_match('/\.\./', $fullpath_original_file) || preg_match('/[<>|]/', $fullpath_original_file)) {
194 dol_syslog("Refused to deliver file " . $fullpath_original_file);
195 print "ErrorFileNameInvalid: " . dol_escape_htmltag($original_file);
196 exit;
197 }
198
199 // Find the subdirectory name as the reference
200 $refname = basename(dirname($original_file) . "/");
201
202 $filename = basename($fullpath_original_file);
203 $filename = preg_replace('/\.noexe$/i', '', $filename);
204
205 // Output file on browser
206 dol_syslog("document controller download $fullpath_original_file filename=$filename content-type=$type");
207 $fullpath_original_file_osencoded = dol_osencode($fullpath_original_file); // New file name encoded in OS encoding charset
208
209 // This test if file exists should be useless. We keep it to find bug more easily
210 if (!file_exists($fullpath_original_file_osencoded)) {
211 dol_syslog("ErrorFileDoesNotExists: " . $fullpath_original_file);
212 print "ErrorFileDoesNotExists: " . $original_file;
213 exit;
214 }
215
216 $fileSize = dol_filesize($fullpath_original_file);
217 $fileSizeMaxDefault = 20 * 1024; // 20 Mo by default
218 $fileSizeMax = getDolGlobalInt('MAIN_SECURITY_MAXFILESIZE_DOWNLOADED', $fileSizeMaxDefault);
219 if ($fileSize > $fileSizeMax) {
220 dol_syslog('ErrorFileSizeTooLarge: ' . $fileSize);
221 print 'ErrorFileSizeTooLarge: ' . $fileSize . ' (max ' . $fileSizeMax . ')';
222 exit;
223 }
224
225 // Hooks
226 $hookmanager->initHooks(array('document'));
227 $parameters = array('ecmfile' => $ecmfile, 'modulepart' => $modulepart, 'original_file' => $original_file,
228 'entity' => $entity, 'refname' => $refname, 'fullpath_original_file' => $fullpath_original_file,
229 'filename' => $filename, 'fullpath_original_file_osencoded' => $fullpath_original_file_osencoded);
230 $object = new stdClass();
231 $reshook = $hookmanager->executeHooks('downloadDocument', $parameters, $object, $action); // Note that $action and $object may have been
232 if ($reshook < 0) {
233 $errors = $hookmanager->error . (is_array($hookmanager->errors) ? (!empty($hookmanager->error) ? ', ' : '') . implode(', ', $hookmanager->errors) : '');
234 dol_syslog("document.php - Errors when executing the hook 'downloadDocument' : " . $errors);
235 print "ErrorDownloadDocumentHooks: " . $errors;
236 exit;
237 }
238
239 $this->action = $action;
240 $this->attachment = $attachment;
241 $this->encoding = $encoding;
242 $this->entity = $entity;
243 $this->filename = $filename;
244 $this->fullpath_original_file = $fullpath_original_file;
245 $this->fullpath_original_file_osencoded = $fullpath_original_file_osencoded;
246 $this->modulepart = $modulepart;
247 $this->original_file = $original_file;
248 $this->type = $type;
249 }
250
256 public function checkAccess()
257 {
258 $this->accessRight = true;
259
260 return parent::checkAccess();
261 }
262
269 public function action()
270 {
271 $context = Context::getInstance();
272 if (!$context->controllerInstance->checkAccess()) {
273 return -1;
274 }
275
276 //$context = Context::getInstance();
277 //$context->title = $langs->trans('WebPortalDocumentTitle');
278 //$context->desc = $langs->trans('WebPortalDocumentDesc');
279 //$context->doNotDisplayHeaderBar=1;// hide default header
280
281 $this->init();
282
283 return 1;
284 }
285
291 public function display()
292 {
293 $context = Context::getInstance();
294 if (!$context->controllerInstance->checkAccess()) {
295 $this->display404();
296 return;
297 }
298
299 // initialize
300 $attachment = $this->attachment;
301 $encoding = $this->encoding;
302 $filename = $this->filename;
303 $fullpath_original_file = $this->fullpath_original_file;
304 $fullpath_original_file_osencoded = $this->fullpath_original_file_osencoded;
305 $type = $this->type;
306
307 clearstatcache();
308
309 // Permissions are ok and file found, so we return it
310 top_httphead($type);
311 header('Content-Description: File Transfer');
312 if ($encoding) {
313 header('Content-Encoding: ' . $encoding);
314 }
315 // Add MIME Content-Disposition from RFC 2183 (inline=automatically displayed, attachment=need user action to open)
316 if ($attachment) {
317 header('Content-Disposition: attachment; filename="' . $filename . '"');
318 } else {
319 header('Content-Disposition: inline; filename="' . $filename . '"');
320 }
321 header('Cache-Control: Public, must-revalidate');
322 header('Pragma: public');
323
324 // Send file now
325 header('Content-Length: ' . dol_filesize($fullpath_original_file));
326 readfileLowMemory($fullpath_original_file_osencoded);
327 }
328}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
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 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.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:139
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.