dolibarr 25.0.0-alpha
files.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2008-2012 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2012-2021 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2012-2016 Juanjo Menent <jmenent@2byte.es>
5 * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
6 * Copyright (C) 2016 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
7 * Copyright (C) 2019-2026 Frédéric France <frederic.france@free.fr>
8 * Copyright (C) 2023-2026 Lenin Rivas <lenin.rivas777@gmail.com>
9 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
10 * Copyright (C) 2025 William Mead <william@m34d.com>
11 * Copyright (C) 2026 Jose Martinez <jose.martinez@pichinov.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <https://www.gnu.org/licenses/>.
25 * or see https://www.gnu.org/
26 */
27
40function dol_basename($pathfile)
41{
42 return preg_replace('/^.*\/([^\/]+)$/', '$1', rtrim($pathfile, '/'));
43}
44
65function dol_dir_list($utf8_path, $types = "all", $recursive = 0, $filter = "", $excludefilter = null, $sortcriteria = "name", $sortorder = SORT_ASC, $mode = 0, $nohook = 0, $relativename = "", $donotfollowsymlinks = 0, $nbsecondsold = 0)
66{
67 global $hookmanager;
68 global $object;
69
70 if ($recursive <= 1) { // Avoid too verbose log
71 $error_info = "";
72
73 // Verify filters (only on the first call of the function)
74 $filter_ok = true;
75 if (!empty($filter) && !is_array($filter)) {
76 if (strlen($filter) > 25000) { // Note that limit depends on syntax of filter
77 dol_syslog("Value for filter is too large", LOG_ERR);
78 $filter_ok = false;
79 } else {
80 // Check that all '/' are escaped.
81 if ((int) preg_match('/(?:^|[^\\\\])\//', $filter) > 0) {
82 $excludefilter_ok = false;
83 $error_info .= " error='filter_has_unescaped_slash'";
84 dol_syslog("'$filter' has unescaped '/'", LOG_ERR);
85 }
86 }
87 }
88
89 // Ensure we have an array for the exclusions
90 $excludefilter_ok = true;
91 $exclude_array = ($excludefilter === null || $excludefilter === '') ? array() : (is_array($excludefilter) ? $excludefilter : array($excludefilter));
92 foreach ($exclude_array as $f) {
93 // Check that all '/' are escaped.
94 if ((int) preg_match('/(?:^|[^\\\\])\//', $f) > 0) {
95 $excludefilter_ok = false;
96 $error_info .= " error='excludefilter_has_unescaped_slash'";
97 dol_syslog("'$f' has unescaped '/'", LOG_ERR);
98 }
99 }
100
101 dol_syslog("files.lib.php::dol_dir_list path=".$utf8_path." types=".$types." recursive=".$recursive." filter=".json_encode($filter)." excludefilter=".json_encode($excludefilter).$error_info);
102 // print 'xxx'."files.lib.php::dol_dir_list path=".$utf8_path." types=".$types." recursive=".$recursive." filter=".json_encode($filter)." excludefilter=".json_encode($exclude_array);
103 if (!$filter_ok || !$excludefilter_ok) {
104 // Return empty array when filters are invalid
105 return array();
106 }
107 } else {
108 // Already computed before
109 $exclude_array = ($excludefilter === null || $excludefilter === '') ? array() : (is_array($excludefilter) ? $excludefilter : array($excludefilter));
110 }
111
112 // Define excludefilterarray (before while, for speed)
113 $excludefilterarray = array_merge(array('^\.'), $exclude_array);
114
115 $loaddate = ($mode == 1 || $mode == 2 || $nbsecondsold != 0 || $sortcriteria == 'date');
116 $loadsize = ($mode == 1 || $mode == 3 || $sortcriteria == 'size');
117 $loadperm = ($mode == 1 || $mode == 4 || $sortcriteria == 'perm');
118
119 $now = dol_now();
120 $reshook = 0;
121 $file_list = array();
122
123 // Clean parameters
124 $utf8_path = preg_replace('/([\\/]+)$/', '', $utf8_path);
125
126 if (preg_match('/\*/', $utf8_path)) {
127 $utf8_path_array = glob($utf8_path, GLOB_ONLYDIR); // This scan dir for files. If file does not exists, return empty.
128 //$os_path_array = dol_dir_list($utf8_path);
129 } else {
130 $utf8_path_array = array($utf8_path);
131 }
132
133 foreach ($utf8_path_array as $utf8_path_cursor) {
134 $os_path = dol_osencode($utf8_path_cursor);
135 if (!$nohook && $hookmanager instanceof HookManager) {
136 $hookmanager->resArray = array();
137
138 $hookmanager->initHooks(array('fileslib'));
139
140 $parameters = array(
141 'path' => $os_path,
142 'types' => $types,
143 'recursive' => $recursive,
144 'filter' => $filter,
145 'excludefilter' => $exclude_array, // Already converted to array.
146 'sortcriteria' => $sortcriteria,
147 'sortorder' => $sortorder,
148 'loaddate' => $loaddate,
149 'loadsize' => $loadsize,
150 'mode' => $mode
151 );
152 $reshook = $hookmanager->executeHooks('getDirList', $parameters, $object);
153 }
154
155 // $hookmanager->resArray may contain array stacked by other modules
156 if (empty($reshook)) {
157 if (!is_dir($os_path)) {
158 continue;
159 }
160
161 if (($dir = opendir($os_path)) === false) {
162 continue;
163 }
164
165 $filedate = '';
166 $filesize = '';
167 $fileperm = '';
168
169 while (false !== ($os_file = readdir($dir))) { // $utf8_file is always a basename (in directory $os_path)
170 $os_fullpathfile = ($os_path ? $os_path.'/' : '').$os_file;
171
172 if (!utf8_check($os_file)) {
173 $utf8_file = mb_convert_encoding($os_file, 'UTF-8', 'ISO-8859-1'); // Make sure data is stored in utf8 in memory
174 } else {
175 $utf8_file = $os_file;
176 }
177
178 $utf8_fullpathfile = $utf8_path_cursor."/".$utf8_file; // Temp variable for speed
179
180 // Check if file is qualified
181 $qualified = 1;
182 foreach ($excludefilterarray as $filt) {
183 if (preg_match('/'.$filt.'/i', $utf8_file) || preg_match('/'.$filt.'/i', $utf8_fullpathfile)) {
184 $qualified = 0;
185 break;
186 }
187 }
188 //print $utf8_fullpathfile.' '.$utf8_file.' '.$qualified.'<br>';
189
190 if ($qualified) {
191 $isdir = is_dir($os_fullpathfile);
192 // Check whether this is a file or directory and whether we're interested in that type
193 if ($isdir) {
194 // Add entry into file_list array
195 if (($types == "directories") || ($types == "all")) {
196 if ($loaddate || $sortcriteria == 'date') {
197 $filedate = dol_filemtime($utf8_fullpathfile);
198 }
199 if ($loadsize || $sortcriteria == 'size') {
200 $filesize = dol_filesize($utf8_fullpathfile);
201 }
202 if ($loadperm || $sortcriteria == 'perm') {
203 $fileperm = dol_fileperm($utf8_fullpathfile);
204 }
205
206 $qualifiedforfilter = 0;
207 if (empty($filter)) {
208 $qualifiedforfilter = 1;
209 } else {
210 $testpregmatch = false;
211 if (is_array($filter)) {
212 $chunks = array_chunk($filter, 500);
213 foreach ($chunks as $chunk) {
214 $testpregmatch = preg_match('/'.implode('|', $chunk).'/i', $utf8_file); // May failed if $filter too large
215 if ($testpregmatch) {
216 break;
217 }
218 }
219 } else {
220 $testpregmatch = preg_match('/'.$filter.'/i', $utf8_file); // May failed if $filter too large
221 }
222 if ($testpregmatch) {
223 $qualifiedforfilter = 1;
224 }
225 }
226
227 if ($qualifiedforfilter) { // We do not search key $filter into all $path, only into $file part
228 $reg = array();
229 preg_match('/([^\/]+)\/[^\/]+$/', $utf8_fullpathfile, $reg);
230 $level1name = (isset($reg[1]) ? $reg[1] : '');
231 $file_list[] = array(
232 "name" => $utf8_file,
233 "path" => $utf8_path,
234 "level1name" => $level1name,
235 "relativename" => ($relativename ? $relativename.'/' : '').$utf8_file,
236 "fullname" => $utf8_fullpathfile,
237 "date" => $filedate,
238 "size" => $filesize,
239 "perm" => $fileperm,
240 "type" => 'dir'
241 );
242 }
243 }
244
245 // if we're in a directory and we want recursive behavior, call this function again
246 if ($recursive > 0) {
247 if (empty($donotfollowsymlinks) || !is_link($os_fullpathfile)) {
248 //var_dump('eee '. $utf8_fullpathfile. ' '.is_dir($utf8_fullpathfile).' '.is_link($utf8_fullpathfile));
249 $file_list = array_merge($file_list, dol_dir_list($utf8_fullpathfile, $types, $recursive + 1, $filter, $exclude_array, $sortcriteria, $sortorder, $mode, $nohook, ($relativename != '' ? $relativename.'/' : '').$utf8_file, $donotfollowsymlinks, $nbsecondsold));
250 }
251 }
252 } elseif (in_array($types, array("files", "all"))) {
253 // Add file into file_list array
254 if ($loaddate || $sortcriteria == 'date') {
255 $filedate = dol_filemtime($utf8_fullpathfile);
256 }
257 if ($loadsize || $sortcriteria == 'size') {
258 $filesize = dol_filesize($utf8_fullpathfile);
259 }
260
261 $qualifiedforfilter = 0;
262 if (empty($filter)) {
263 $qualifiedforfilter = 1;
264 } else {
265 $testpregmatch = false;
266 if (is_array($filter)) {
267 $chunks = array_chunk($filter, 500);
268 foreach ($chunks as $chunk) {
269 $testpregmatch = preg_match('/'.implode('|', $chunk).'/i', $utf8_file); // May failed if $filter too large
270 if ($testpregmatch) {
271 break;
272 }
273 }
274 } else {
275 $testpregmatch = preg_match('/'.$filter.'/i', $utf8_file); // May failed if $filter too large
276 }
277 if ($testpregmatch) {
278 $qualifiedforfilter = 1;
279 }
280 }
281
282 if ($qualifiedforfilter) { // We do not search key $filter into all $path, only into $file part
283 if (empty($nbsecondsold) || $filedate <= ($now - $nbsecondsold)) {
284 preg_match('/([^\/]+)\/[^\/]+$/', $utf8_fullpathfile, $reg);
285 $level1name = (isset($reg[1]) ? $reg[1] : '');
286 $file_list[] = array(
287 "name" => $utf8_file,
288 "path" => $utf8_path,
289 "level1name" => $level1name,
290 "relativename" => ($relativename ? $relativename.'/' : '').$utf8_file,
291 "fullname" => $utf8_fullpathfile,
292 "date" => $filedate,
293 "size" => $filesize,
294 "type" => 'file'
295 );
296 }
297 }
298 }
299 }
300 }
301 closedir($dir);
302 }
303 }
304
305 // Obtain a list of columns
306 if (!empty($sortcriteria) && $sortorder) {
307 $file_list = dol_sort_array($file_list, $sortcriteria, ($sortorder == SORT_ASC ? 'asc' : 'desc'));
308 }
309
310 if ($hookmanager instanceof HookManager && is_array($hookmanager->resArray)) {
311 $file_list = array_merge($file_list, $hookmanager->resArray);
312 }
313
314 return $file_list;
315}
316
317
334function dol_dir_list_in_database($path, $filter = "", $excludefilter = null, $sortcriteria = "name", $sortorder = SORT_ASC, $mode = 0, $sqlfilters = "", $object = null)
335{
336 global $conf, $db;
337
338 if (is_null($object)) {
339 $object = new stdClass();
340 }
341
342 $sql = "SELECT rowid, label, entity, filename, filepath, fullpath_orig, keywords, cover, gen_or_uploaded, extraparams,";
343 $sql .= " date_c, tms as date_m, fk_user_c, fk_user_m, acl, position, share";
344 if ($mode) {
345 $sql .= ", description";
346 }
347 $sql .= " FROM ".MAIN_DB_PREFIX."ecm_files";
348 if (!empty($object->entity)) {
349 $sql .= " WHERE entity = ".((int) $object->entity);
350 } else {
351 $sql .= " WHERE entity = ".((int) $conf->entity);
352 }
353 if (preg_match('/%$/', $path)) {
354 $sql .= " AND (filepath LIKE '".$db->escape($path)."' OR filepath = '".$db->escape(preg_replace('/\/%$/', '', $path))."')";
355 } else {
356 $sql .= " AND filepath = '".$db->escape($path)."'";
357 }
358
359 // Manage filter
360 $errormessage = '';
361 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
362 if ($errormessage) {
363 dol_print_error(null, $errormessage);
364 return array();
365 }
366
367 $resql = $db->query($sql);
368 if ($resql) {
369 $file_list = array();
370 $num = $db->num_rows($resql);
371 $i = 0;
372 while ($i < $num) {
373 $obj = $db->fetch_object($resql);
374 if ($obj) {
375 $reg = array();
376 preg_match('/([^\/]+)\/[^\/]+$/', DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename, $reg);
377 $level1name = (isset($reg[1]) ? $reg[1] : '');
378 $file_list[] = array(
379 "rowid" => $obj->rowid,
380 "label" => $obj->label, // md5
381 "name" => $obj->filename,
382 "path" => DOL_DATA_ROOT.'/'.$obj->filepath,
383 "level1name" => $level1name,
384 "fullname" => DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename,
385 "fullpath_orig" => $obj->fullpath_orig,
386 "date_c" => $db->jdate($obj->date_c),
387 "date_m" => $db->jdate($obj->date_m),
388 "type" => 'file',
389 "keywords" => $obj->keywords,
390 "cover" => $obj->cover,
391 "position" => (int) $obj->position,
392 "acl" => $obj->acl,
393 "share" => $obj->share,
394 "description" => ($mode ? $obj->description : '')
395 // TODO Add 'content' with $mode == 2 ?
396 );
397 }
398 $i++;
399 }
400
401 // Obtain a list of columns
402 if (!empty($sortcriteria)) {
403 $myarray = array();
404 foreach ($file_list as $key => $row) {
405 $myarray[$key] = (isset($row[$sortcriteria]) ? $row[$sortcriteria] : '');
406 }
407 // Sort the data
408 if ($sortorder) {
409 array_multisort($myarray, $sortorder, SORT_REGULAR, $file_list);
410 }
411 }
412
413 return $file_list;
414 } else {
415 dol_print_error($db);
416 return array();
417 }
418}
419
420
430function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir, $object = null)
431{
432 global $conf, $db, $user;
433
434 if (is_null($object)) {
435 $object = new stdClass();
436 $object->id = null;
437 $object->element = null;
438 }
439
440 $filearrayindatabase = dol_dir_list_in_database(rtrim($relativedir, "/\\"), '', null, 'name', SORT_ASC, 0, '', $object);
441
442 global $modulepart;
443 // Note: $modulepart is 'product' when set by product/document.php, but 'produit' in some other contexts, so we accept both.
444 if (in_array($modulepart, array('produit', 'product')) && getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
445 // TODO Remove this when PRODUCT_USE_OLD_PATH_FOR_PHOTO will be removed
446 global $object;
447 if (!empty($object->id)) {
448 if (isModEnabled("product")) {
449 $upload_dirold = $conf->product->multidir_output[$object->entity ?? $conf->entity].'/'.substr(substr("000".$object->id, -2), 1, 1).'/'.substr(substr("000".$object->id, -2), 0, 1).'/'.$object->id."/photos";
450 } else {
451 $upload_dirold = $conf->service->multidir_output[$object->entity ?? $conf->entity].'/'.substr(substr("000".$object->id, -2), 1, 1).'/'.substr(substr("000".$object->id, -2), 0, 1).'/'.$object->id."/photos";
452 }
453
454 $relativedirold = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dirold);
455 $relativedirold = ltrim($relativedirold, "/\\");
456
457 // Note: $object must be provided so the entity filter matches the one used to forge $upload_dirold (multicompany)
458 $filearrayindatabase = array_merge($filearrayindatabase, dol_dir_list_in_database($relativedirold, '', null, 'name', SORT_ASC, 0, '', $object));
459 }
460 } elseif ($modulepart == 'ticket') {
461 foreach ($filearray as $key => $val) {
462 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filearray[$key]['path']);
463 $rel_dir = trim($rel_dir, "/\\");
464 if ($rel_dir != $relativedir) {
465 $filearrayindatabase = array_merge($filearrayindatabase, dol_dir_list_in_database($rel_dir, '', null, 'name', SORT_ASC));
466 }
467 }
468 }
469
470 // Complete filearray with properties found into $filearrayindatabase
471 foreach ($filearray as $key => $val) {
472 $tmpfilename = preg_replace('/\.noexe$/', '', $filearray[$key]['name']);
473 $found = 0;
474 // Search if it exists into $filearrayindatabase
475 foreach ($filearrayindatabase as $key2 => $val2) {
476 if (($filearrayindatabase[$key2]['path'] == $filearray[$key]['path']) && ($filearrayindatabase[$key2]['name'] == $tmpfilename)) {
477 $filearray[$key]['position_name'] = ($filearrayindatabase[$key2]['position'] ? $filearrayindatabase[$key2]['position'] : '0').'_'.$filearrayindatabase[$key2]['name'];
478 $filearray[$key]['position'] = $filearrayindatabase[$key2]['position'];
479 $filearray[$key]['cover'] = $filearrayindatabase[$key2]['cover'];
480 $filearray[$key]['keywords'] = $filearrayindatabase[$key2]['keywords'];
481 $filearray[$key]['acl'] = $filearrayindatabase[$key2]['acl'];
482 $filearray[$key]['rowid'] = $filearrayindatabase[$key2]['rowid'];
483 $filearray[$key]['label'] = $filearrayindatabase[$key2]['label'];
484 $filearray[$key]['share'] = $filearrayindatabase[$key2]['share'];
485 $found = 1;
486 break;
487 }
488 }
489
490 if (!$found) { // This happen in transition toward version 6, or if files were added manually into os dir.
491 $filearray[$key]['position'] = '999999'; // File not indexed are at end. So if we add a file, it will not replace an existing position
492 $filearray[$key]['cover'] = 0;
493 $filearray[$key]['acl'] = '';
494 $filearray[$key]['share'] = 0;
495
496 $rel_filename = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filearray[$key]['fullname']);
497
498 if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filename)) { // If not a tmp file
499 dol_syslog("list_of_documents We found a file called '".$filearray[$key]['name']."' not indexed into database. We add it");
500
501 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
502 $ecmfile = new EcmFiles($db);
503
504 // Add entry into database
505 $filename = basename($rel_filename);
506 $rel_dir = dirname($rel_filename);
507 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
508 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
509
510 $ecmfile->filepath = $rel_dir;
511 $ecmfile->filename = $filename;
512 $ecmfile->label = md5_file(dol_osencode($filearray[$key]['fullname'])); // $destfile is a full path to file
513 $ecmfile->fullpath_orig = $filearray[$key]['fullname'];
514 $ecmfile->gen_or_uploaded = 'unknown';
515 if (is_object($object)) {
516 $ecmfile->src_object_type = $object->element;
517 $ecmfile->src_object_id = $object->id;
518 }
519 $ecmfile->description = ''; // indexed content
520 $ecmfile->keywords = ''; // keyword content
521 // When you scan file with dol_dir_list_in_database, you scan for files in entity of object (like with projects), even if you
522 // are connected into another entity. So we must also create record that was not found into the entity scan, so the one of the object).
523 $ecmfile->entity = empty($object->entity) ? $conf->entity : $object->entity;
524
525 $result = $ecmfile->create($user);
526 if ($result < 0) {
527 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
528 } else {
529 $filearray[$key]['rowid'] = $result;
530 }
531 } else {
532 $filearray[$key]['rowid'] = 0; // Should not happened
533 }
534 }
535 }
536 //var_dump($filearray); var_dump($relativedir.' - tmpfilename='.$tmpfilename.' - found='.$found);
537}
538
539
547function dol_compare_file($a, $b)
548{
549 global $sortorder, $sortfield;
550
551 $sortorder = strtoupper($sortorder);
552
553 if ($sortorder == 'ASC') {
554 $retup = -1;
555 $retdown = 1;
556 } else {
557 $retup = 1;
558 $retdown = -1;
559 }
560
561 if ($sortfield == 'name') {
562 if ($a->name == $b->name) {
563 return 0;
564 }
565 return ($a->name < $b->name) ? $retup : $retdown;
566 }
567 if ($sortfield == 'date') {
568 if ($a->date == $b->date) {
569 return 0;
570 }
571 return ($a->date < $b->date) ? $retup : $retdown;
572 }
573 if ($sortfield == 'size') {
574 if ($a->size == $b->size) {
575 return 0;
576 }
577 return ($a->size < $b->size) ? $retup : $retdown;
578 }
579
580 return 0;
581}
582
583
590function dol_is_dir($folder)
591{
592 $newfolder = dol_osencode($folder);
593 if (is_dir($newfolder)) {
594 return true;
595 } else {
596 return false;
597 }
598}
599
606function dol_is_dir_empty($dir)
607{
608 if (!is_readable($dir)) {
609 return false;
610 }
611 return (count(scandir($dir)) == 2);
612}
613
620function dol_is_file($pathoffile)
621{
622 $newpathoffile = dol_osencode($pathoffile);
623 return is_file($newpathoffile);
624}
625
632function dol_is_link($pathoffile)
633{
634 $newpathoffile = dol_osencode($pathoffile);
635 return is_link($newpathoffile);
636}
637
644function dol_is_writable($folderorfile)
645{
646 $newfolderorfile = dol_osencode($folderorfile);
647 return is_writable($newfolderorfile);
648}
649
658function dol_is_url($uri)
659{
660 $prots = array('file', 'http', 'https', 'ftp', 'zlib', 'data', 'ssh', 'ssh2', 'ogg', 'expect');
661 return false !== preg_match('/^('.implode('|', $prots).'):/i', $uri);
662}
663
670function dol_dir_is_emtpy($folder)
671{
672 $newfolder = dol_osencode($folder);
673 if (is_dir($newfolder)) {
674 $handle = opendir($newfolder);
675 $folder_content = '';
676 $name_array = [];
677 while ((gettype($name = readdir($handle)) != "boolean")) {
678 $name_array[] = $name;
679 }
680 foreach ($name_array as $temp) {
681 $folder_content .= $temp;
682 }
683
684 closedir($handle);
685
686 if ($folder_content == "...") {
687 return true;
688 } else {
689 return false;
690 }
691 } else {
692 return true; // Dir does not exists
693 }
694}
695
703function dol_count_nb_of_line($file)
704{
705 $nb = 0;
706
707 $newfile = dol_osencode($file);
708 //print 'x'.$file;
709 $fp = fopen($newfile, 'r');
710 if ($fp) {
711 while (!feof($fp)) {
712 $line = fgets($fp);
713 // Increase count only if read was success.
714 // Test needed because feof returns true only after fgets
715 // so we do n+1 fgets for a file with n lines.
716 if ($line !== false) {
717 $nb++;
718 }
719 }
720 fclose($fp);
721 } else {
722 $nb = -1;
723 }
724
725 return $nb;
726}
727
728
736function dol_filesize($pathoffile)
737{
738 $newpathoffile = dol_osencode($pathoffile);
739 return filesize($newpathoffile);
740}
741
748function dol_filemtime($pathoffile)
749{
750 $newpathoffile = dol_osencode($pathoffile);
751 return @filemtime($newpathoffile); // @Is to avoid errors if files does not exists
752}
753
760function dol_fileperm($pathoffile)
761{
762 $newpathoffile = dol_osencode($pathoffile);
763 return fileperms($newpathoffile);
764}
765
778function dolReplaceInFile($srcfile, $arrayreplacement, $destfile = '', $newmask = '0', $indexdatabase = 0, $arrayreplacementisregex = 0)
779{
780 dol_syslog("files.lib.php::dolReplaceInFile srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." indexdatabase=".$indexdatabase." arrayreplacementisregex=".$arrayreplacementisregex);
781
782 if (empty($srcfile)) {
783 return -1;
784 }
785 if (empty($destfile)) {
786 $destfile = $srcfile;
787 }
788
789 // Clean the aa/bb/../cc into aa/cc
790 $srcfile = preg_replace('/\.\.\/?/', '', $srcfile);
791 $destfile = preg_replace('/\.\.\/?/', '', $destfile);
792
793 $destexists = dol_is_file($destfile);
794 if (($destfile != $srcfile) && $destexists) {
795 return 0;
796 }
797
798 $srcexists = dol_is_file($srcfile);
799 if (!$srcexists) {
800 dol_syslog("files.lib.php::dolReplaceInFile failed to read src file", LOG_WARNING);
801 return -3;
802 }
803
804 $tmpdestfile = $destfile.'.tmp';
805
806 $newpathofsrcfile = dol_osencode($srcfile);
807 $newpathoftmpdestfile = dol_osencode($tmpdestfile);
808 $newpathofdestfile = dol_osencode($destfile);
809 $newdirdestfile = dirname($newpathofdestfile);
810
811 if ($destexists && !is_writable($newpathofdestfile)) {
812 dol_syslog("files.lib.php::dolReplaceInFile failed Permission denied to overwrite target file", LOG_WARNING);
813 return -1;
814 }
815 if (!is_writable($newdirdestfile)) {
816 dol_syslog("files.lib.php::dolReplaceInFile failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING);
817 return -2;
818 }
819
820 dol_delete_file($tmpdestfile);
821
822 // Create $newpathoftmpdestfile from $newpathofsrcfile
823 $content = file_get_contents($newpathofsrcfile);
824
825 if (empty($arrayreplacementisregex)) {
826 $content = make_substitutions($content, $arrayreplacement, null);
827 } else {
828 foreach ($arrayreplacement as $key => $value) {
829 $content = preg_replace($key, (string) $value, $content);
830 }
831 }
832
833 file_put_contents($newpathoftmpdestfile, $content);
834 dolChmod($newpathoftmpdestfile, $newmask);
835
836 // Rename
837 $moreinfo = array('gen_or_uploaded' => 'unknown');
838 $result = dol_move($newpathoftmpdestfile, $newpathofdestfile, $newmask, (($destfile == $srcfile) ? 1 : 0), 0, $indexdatabase, $moreinfo);
839 if (!$result) {
840 dol_syslog("files.lib.php::dolReplaceInFile failed to move tmp file to final dest", LOG_WARNING);
841 return -3;
842 }
843 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
844 $newmask = getDolGlobalString('MAIN_UMASK');
845 }
846 if (empty($newmask)) { // This should no happen
847 dol_syslog("Warning: dolReplaceInFile called with empty value for newmask and no default value defined", LOG_WARNING);
848 $newmask = '0664';
849 }
850
851 dolChmod($newpathofdestfile, $newmask);
852
853 return 1;
854}
855
863function removePatternFromFile(string $filePath, string $pattern): bool
864{
865 // Check if the file exists
866 if (! file_exists($filePath)) {
867 dol_syslog("files.lib.php::removePatternFromFile: File $filePath does not exist", LOG_WARNING);
868
869 return false;
870 }
871
872 // Read the file content
873 $content = file_get_contents($filePath);
874 if ($content === false) {
875 dol_syslog("files.lib.php::removePatternFromFile: Unable to read the file $filePath", LOG_WARNING);
876
877 return false;
878 }
879
880 // Remove content matching the pattern
881 $updatedContent = preg_replace($pattern, '', $content);
882 if ($updatedContent === null) {
883 dol_syslog("files.lib.php::removePatternFromFile: Error while processing the file $filePath", LOG_WARNING);
884
885 return false;
886 }
887
888 // Write the updated content back to the file
889 $result = file_put_contents($filePath, $updatedContent);
890 if ($result === false) {
891 dol_syslog("files.lib.php::removePatternFromFile: Permission denied to overwrite the target file $filePath", LOG_WARNING);
892
893 return false;
894 }
895
896 dol_syslog("files.lib.php::removePatternFromFile: Content successfully removed in the file $filePath", LOG_INFO);
897
898 return true;
899}
900
901
902
915function dol_copy($srcfile, $destfile, $newmask = '0', $overwriteifexists = 1, $testvirus = 0, $indexdatabase = 0)
916{
917 global $db, $user;
918
919 dol_syslog("files.lib.php::dol_copy srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists);
920
921 if (empty($srcfile) || empty($destfile)) {
922 return -1;
923 }
924
925 $destexists = dol_is_file($destfile);
926 if (!$overwriteifexists && $destexists) {
927 return 0;
928 }
929
930 $newpathofsrcfile = dol_osencode($srcfile);
931 $newpathofdestfile = dol_osencode($destfile);
932 $newdirdestfile = dirname($newpathofdestfile);
933
934 if ($destexists && !is_writable($newpathofdestfile)) {
935 dol_syslog("files.lib.php::dol_copy failed Permission denied to overwrite target file", LOG_WARNING);
936 return -1;
937 }
938 if (!is_writable($newdirdestfile)) {
939 dol_syslog("files.lib.php::dol_copy failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING);
940 return -2;
941 }
942
943 // Check virus
944 $testvirusarray = array();
945 if ($testvirus) {
946 $testvirusarray = dolCheckVirus($srcfile, $destfile);
947 if (count($testvirusarray)) {
948 dol_syslog("files.lib.php::dol_copy canceled because a virus was found into source file. we ignore the copy request.", LOG_WARNING);
949 return -3;
950 }
951 }
952
953 // Copy with overwriting if exists
954 $result = @copy($newpathofsrcfile, $newpathofdestfile);
955 //$result=copy($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
956 if (!$result) {
957 dol_syslog("files.lib.php::dol_copy failed to copy", LOG_WARNING);
958 return -3;
959 }
960 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
961 $newmask = getDolGlobalString('MAIN_UMASK');
962 }
963 if (empty($newmask)) { // This should no happen
964 dol_syslog("Warning: dol_copy called with empty value for newmask and no default value defined", LOG_WARNING);
965 $newmask = '0664';
966 }
967
968 dolChmod($newpathofdestfile, $newmask);
969
970 if ($result && $indexdatabase) {
971 // Add entry into ecm database
972 $rel_filetocopyafter = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $newpathofdestfile);
973 if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetocopyafter)) { // If not a tmp file
974 $rel_filetocopyafter = preg_replace('/^[\\/]/', '', $rel_filetocopyafter);
975 //var_dump($rel_filetorenamebefore.' - '.$rel_filetocopyafter);exit;
976
977 dol_syslog("Try to copy also entries in database for: ".$rel_filetocopyafter, LOG_DEBUG);
978 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
979
980 $ecmfiletarget = new EcmFiles($db);
981 $resultecmtarget = $ecmfiletarget->fetch(0, '', $rel_filetocopyafter);
982 if ($resultecmtarget > 0) { // An entry for target name already exists for target, we delete it, a new one will be created.
983 dol_syslog("ECM dest file found, remove it", LOG_DEBUG);
984 $ecmfiletarget->delete($user);
985 } else {
986 dol_syslog("ECM dest file not found, create it", LOG_DEBUG);
987 }
988
989 $ecmSrcfile = new EcmFiles($db);
990 $resultecm = $ecmSrcfile->fetch(0, '', $srcfile);
991 if ($resultecm) {
992 dol_syslog("Fetch src file ok", LOG_DEBUG);
993 } else {
994 dol_syslog("Fetch src file error", LOG_DEBUG);
995 }
996
997 $ecmfile = new EcmFiles($db);
998 $filename = basename($rel_filetocopyafter);
999 $rel_dir = dirname($rel_filetocopyafter);
1000 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
1001 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
1002
1003 $ecmfile->filepath = $rel_dir;
1004 $ecmfile->filename = $filename;
1005 $ecmfile->label = md5_file(dol_osencode($destfile)); // $destfile is a full path to file
1006 $ecmfile->fullpath_orig = $srcfile;
1007 $ecmfile->gen_or_uploaded = 'copy';
1008 $ecmfile->description = $ecmSrcfile->description;
1009 $ecmfile->keywords = $ecmSrcfile->keywords;
1010 $resultecm = $ecmfile->create($user);
1011 if ($resultecm < 0) {
1012 dol_syslog("Create ECM file ok", LOG_DEBUG);
1013 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1014 } else {
1015 dol_syslog("Create ECM file error", LOG_DEBUG);
1016 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1017 }
1018
1019 if ($resultecm > 0) {
1020 $result = 1;
1021 } else {
1022 $result = -1;
1023 }
1024 }
1025 }
1026
1027 return (int) $result;
1028}
1029
1044function dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists, $arrayreplacement = null, $excludesubdir = 0, $excludefileext = null, $excludearchivefiles = 0)
1045{
1046 $result = 0;
1047
1048 dol_syslog("files.lib.php::dolCopyDir srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists);
1049
1050 if (empty($srcfile) || empty($destfile)) {
1051 return -1;
1052 }
1053
1054 $destexists = dol_is_dir($destfile);
1055
1056 //if (! $overwriteifexists && $destexists) return 0; // The overwriteifexists is for files only, so propagated to dol_copy only.
1057
1058 if (!$destexists) {
1059 // We must set mask just before creating dir, because it can be set differently by dol_copy
1060 umask(0);
1061 $dirmaskdec = octdec($newmask);
1062 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
1063 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK'));
1064 }
1065 $dirmaskdec |= octdec('0200'); // Set w bit required to be able to create content for recursive subdirs files
1066
1067 $result = dol_mkdir($destfile, '', decoct($dirmaskdec));
1068
1069 if (!dol_is_dir($destfile)) {
1070 // The output directory does not exists and we failed to create it. So we stop here.
1071 return -1;
1072 }
1073 }
1074
1075 $ossrcfile = dol_osencode($srcfile);
1076 $osdestfile = dol_osencode($destfile);
1077
1078 // Recursive function to copy all subdirectories and contents:
1079 if (is_dir($ossrcfile)) {
1080 $dir_handle = opendir($ossrcfile);
1081 $tmpresult = 0; // Initialised before loop to keep old behavior, may be needed inside loop
1082 while ($file = readdir($dir_handle)) {
1083 if ($file != "." && $file != ".." && !is_link($ossrcfile."/".$file)) {
1084 if (is_dir($ossrcfile."/".$file)) {
1085 if (empty($excludesubdir) || ($excludesubdir == 2 && dol_strlen($file) == 2)) {
1086 $newfile = $file;
1087 // Replace destination filename with a new one
1088 if (is_array($arrayreplacement)) {
1089 foreach ($arrayreplacement as $key => $val) {
1090 $newfile = str_replace($key, $val, $newfile);
1091 }
1092 }
1093 //var_dump("xxx dolCopyDir $srcfile/$file, $destfile/$file, $newmask, $overwriteifexists");
1094 $tmpresult = dolCopyDir($srcfile."/".$file, $destfile."/".$newfile, $newmask, $overwriteifexists, $arrayreplacement, $excludesubdir, $excludefileext, $excludearchivefiles);
1095 }
1096 } else {
1097 $newfile = $file;
1098
1099 if (is_array($excludefileext)) {
1100 $extension = pathinfo($file, PATHINFO_EXTENSION);
1101 if (in_array($extension, $excludefileext)) {
1102 //print "We exclude the file ".$file." because its extension is inside list ".join(', ', $excludefileext); exit;
1103 continue;
1104 }
1105 }
1106
1107 if ($excludearchivefiles == 1) {
1108 $extension = pathinfo($file, PATHINFO_EXTENSION);
1109 if (preg_match('/^[v|d]\d+$/', $extension)) {
1110 continue;
1111 }
1112 }
1113
1114 // Replace destination filename with a new one
1115 if (is_array($arrayreplacement)) {
1116 foreach ($arrayreplacement as $key => $val) {
1117 $newfile = str_replace($key, $val, $newfile);
1118 }
1119 }
1120 $tmpresult = dol_copy($srcfile."/".$file, $destfile."/".$newfile, $newmask, $overwriteifexists);
1121 }
1122 // Set result
1123 if ($result > 0 && $tmpresult >= 0) {
1124 // Do nothing, so we don't set result to 0 if tmpresult is 0 and result was success in a previous pass
1125 } else {
1126 $result = $tmpresult;
1127 }
1128 if ($result < 0) {
1129 break;
1130 }
1131 }
1132 }
1133 closedir($dir_handle);
1134 } else {
1135 // Source directory does not exists
1136 $result = -2;
1137 }
1138
1139 return (int) $result;
1140}
1141
1142
1161function dol_move($srcfile, $destfile, $newmask = '0', $overwriteifexists = 1, $testvirus = 0, $indexdatabase = 1, $moreinfo = array(), $entity = null)
1162{
1163 global $user, $db;
1164 $result = false;
1165
1166 dol_syslog("files.lib.php::dol_move srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwritifexists=".$overwriteifexists);
1167 $srcexists = dol_is_file($srcfile);
1168 $destexists = dol_is_file($destfile);
1169
1170 if (!$srcexists) {
1171 dol_syslog("files.lib.php::dol_move srcfile does not exists. we ignore the move request.");
1172 return false;
1173 }
1174
1175 if ($overwriteifexists || !$destexists) {
1176 $newpathofsrcfile = dol_osencode($srcfile);
1177 $newpathofdestfile = dol_osencode($destfile);
1178
1179 // Check on virus
1180 $testvirusarray = array();
1181 if ($testvirus) {
1182 // Check using filename + antivirus
1183 $testvirusarray = dolCheckVirus($newpathofsrcfile, $newpathofdestfile);
1184 if (count($testvirusarray)) {
1185 dol_syslog("files.lib.php::dol_move canceled because a virus was found into source file. We ignore the move request.", LOG_WARNING);
1186 return false;
1187 }
1188 } else {
1189 // Check using filename only
1190 $testvirusarray = dolCheckOnFileName($newpathofsrcfile, $newpathofdestfile);
1191 if (count($testvirusarray)) {
1192 dol_syslog("files.lib.php::dol_move canceled because a virus was found into source file. We ignore the move request.", LOG_WARNING);
1193 return false;
1194 }
1195 }
1196
1197 global $dolibarr_main_restrict_os_commands;
1198 if (!empty($dolibarr_main_restrict_os_commands)) {
1199 $arrayofallowedcommand = explode(',', $dolibarr_main_restrict_os_commands);
1200 $arrayofallowedcommand = array_map('trim', $arrayofallowedcommand);
1201 if (in_array(basename($destfile), $arrayofallowedcommand)) {
1202 //$langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
1203 //setEventMessages($langs->trans("ErrorFilenameReserved", basename($destfile)), null, 'errors');
1204 dol_syslog("files.lib.php::dol_move canceled because target filename ".basename($destfile)." is using a reserved command name. we ignore the move request.", LOG_WARNING);
1205 return false;
1206 }
1207 }
1208
1209 $result = @rename($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
1210 if (!$result) {
1211 if ($destexists) {
1212 dol_syslog("files.lib.php::dol_move Failed. We try to delete target first and move after.", LOG_WARNING);
1213 // We force delete and try again. Rename function sometimes fails to replace dest file with some windows NTFS partitions.
1214 dol_delete_file($destfile);
1215 $result = @rename($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
1216 } else {
1217 dol_syslog("files.lib.php::dol_move Failed.", LOG_WARNING);
1218 }
1219 }
1220
1221 // Move ok
1222 if ($result && $indexdatabase) {
1223 // Rename entry into ecm database
1224 $rel_filetorenamebefore = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $srcfile);
1225 $rel_filetorenameafter = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $destfile);
1226 if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetorenameafter)) { // If not a tmp file
1227 $rel_filetorenamebefore = preg_replace('/^[\\/]/', '', $rel_filetorenamebefore);
1228 $rel_filetorenameafter = preg_replace('/^[\\/]/', '', $rel_filetorenameafter);
1229 //var_dump($rel_filetorenamebefore.' - '.$rel_filetorenameafter);exit;
1230
1231 dol_syslog("Try to rename also entries in database for full relative path before = ".$rel_filetorenamebefore." after = ".$rel_filetorenameafter, LOG_DEBUG);
1232 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1233
1234 $ecmfiletarget = new EcmFiles($db);
1235 $resultecmtarget = $ecmfiletarget->fetch(0, '', $rel_filetorenameafter, '', '', '', 0, $entity);
1236 if ($resultecmtarget > 0) { // An entry for target name already exists for target, we delete it, a new one will be created.
1237 $ecmfiletarget->delete($user);
1238 }
1239
1240 $ecmfile = new EcmFiles($db);
1241 $resultecm = $ecmfile->fetch(0, '', $rel_filetorenamebefore, '', '', '', 0, $entity);
1242 if ($resultecm > 0) { // If an entry was found for src file, we use it to move entry
1243 $filename = basename($rel_filetorenameafter);
1244 $rel_dir = dirname($rel_filetorenameafter);
1245 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
1246 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
1247
1248 $ecmfile->filepath = $rel_dir;
1249 $ecmfile->filename = $filename;
1250
1251 $resultecm = $ecmfile->update($user);
1252 } elseif ($resultecm == 0) { // If no entry were found for src files, create/update target file
1253 $filename = basename($rel_filetorenameafter);
1254 $rel_dir = dirname($rel_filetorenameafter);
1255 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
1256 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
1257
1258 $ecmfile->filepath = $rel_dir;
1259 $ecmfile->filename = $filename;
1260 $ecmfile->label = md5_file(dol_osencode($destfile)); // $destfile is a full path to file
1261 $ecmfile->fullpath_orig = basename($srcfile);
1262 if (!empty($moreinfo) && !empty($moreinfo['gen_or_uploaded'])) {
1263 $ecmfile->gen_or_uploaded = $moreinfo['gen_or_uploaded'];
1264 } else {
1265 $ecmfile->gen_or_uploaded = 'unknown'; // 'generated', 'uploaded', 'api'
1266 }
1267 if (!empty($moreinfo) && !empty($moreinfo['description'])) {
1268 $ecmfile->description = $moreinfo['description']; // indexed content
1269 } else {
1270 $ecmfile->description = ''; // indexed content
1271 }
1272 if (!empty($moreinfo) && !empty($moreinfo['keywords'])) {
1273 $ecmfile->keywords = $moreinfo['keywords']; // indexed content
1274 } else {
1275 $ecmfile->keywords = ''; // keyword content
1276 }
1277 if (!empty($moreinfo) && !empty($moreinfo['note_private'])) {
1278 $ecmfile->note_private = $moreinfo['note_private'];
1279 }
1280 if (!empty($moreinfo) && !empty($moreinfo['note_public'])) {
1281 $ecmfile->note_public = $moreinfo['note_public'];
1282 }
1283 if (!empty($moreinfo) && !empty($moreinfo['src_object_type'])) {
1284 $ecmfile->src_object_type = $moreinfo['src_object_type']; // Usually the $object->table_element
1285 }
1286 if (!empty($moreinfo) && !empty($moreinfo['src_object_id'])) {
1287 $ecmfile->src_object_id = $moreinfo['src_object_id'];
1288 }
1289 if (!empty($moreinfo) && !empty($moreinfo['agenda_id'])) {
1290 $ecmfile->agenda_id = $moreinfo['agenda_id'];
1291 }
1292 if (!empty($moreinfo) && !empty($moreinfo['position'])) {
1293 $ecmfile->position = $moreinfo['position'];
1294 }
1295 if (!empty($moreinfo) && !empty($moreinfo['cover'])) {
1296 $ecmfile->cover = $moreinfo['cover'];
1297 }
1298 if (!empty($moreinfo) && !empty($moreinfo['share'])) {
1299 $ecmfile->share = $moreinfo['share'];
1300 }
1301 if (! empty($entity)) {
1302 $ecmfile->entity = $entity;
1303 }
1304
1305 $resultecm = $ecmfile->create($user);
1306 if ($resultecm < 0) {
1307 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1308 } else {
1309 if (!empty($moreinfo) && !empty($moreinfo['array_options']) && is_array($moreinfo['array_options'])) {
1310 $ecmfile->array_options = $moreinfo['array_options'];
1311 $resultecm = $ecmfile->insertExtraFields();
1312 if ($resultecm < 0) {
1313 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1314 }
1315 }
1316 }
1317 } elseif ($resultecm < 0) {
1318 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1319 }
1320
1321 if ($resultecm > 0) {
1322 $result = true;
1323 } else {
1324 $result = false;
1325 }
1326 }
1327 }
1328
1329 if (empty($newmask)) {
1330 $newmask = getDolGlobalString('MAIN_UMASK', '0755');
1331 }
1332
1333 // Currently method is restricted to files (dol_delete_files previously used is for files, and mask usage if for files too)
1334 // to allow mask usage for dir, we should introduce a new param "isdir" to 1 to complete newmask like this
1335 // if ($isdir) $newmaskdec |= octdec('0111'); // Set x bit required for directories
1336 dolChmod($newpathofdestfile, $newmask);
1337 }
1338
1339 return $result;
1340}
1341
1352function dol_move_dir($srcdir, $destdir, $overwriteifexists = 1, $indexdatabase = 1, $renamedircontent = 1)
1353{
1354 $result = false;
1355
1356 dol_syslog("files.lib.php::dol_move_dir srcdir=".$srcdir." destdir=".$destdir." overwritifexists=".$overwriteifexists." indexdatabase=".$indexdatabase." renamedircontent=".$renamedircontent);
1357 $srcexists = dol_is_dir($srcdir);
1358 $srcbasename = basename($srcdir);
1359 $destexists = dol_is_dir($destdir);
1360
1361 if (!$srcexists) {
1362 dol_syslog("files.lib.php::dol_move_dir srcdir does not exists. Move fails");
1363 return false;
1364 }
1365
1366 if ($overwriteifexists || !$destexists) {
1367 $newpathofsrcdir = dol_osencode($srcdir);
1368 $newpathofdestdir = dol_osencode($destdir);
1369
1370 // On windows, if destination directory exists and is empty, command fails. So if overwrite is on, we first remove destination directory.
1371 // On linux, if destination directory exists and is empty, command succeed. So no need to delete di destination directory first.
1372 // Note: If dir exists and is not empty, it will and must fail on both linux and windows even, if option $overwriteifexists is on.
1373 if ($overwriteifexists) {
1374 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1375 if (is_dir($newpathofdestdir)) {
1376 @rmdir($newpathofdestdir);
1377 }
1378 }
1379 }
1380
1381 $result = @rename($newpathofsrcdir, $newpathofdestdir);
1382
1383 // Now rename contents in the directory after the move to match the new destination
1384 if ($result && $renamedircontent) {
1385 if (file_exists($newpathofdestdir)) {
1386 $destbasename = basename($newpathofdestdir);
1387 $files = dol_dir_list($newpathofdestdir);
1388 if (!empty($files) && is_array($files)) {
1389 foreach ($files as $key => $file) {
1390 if (!file_exists($file["fullname"])) {
1391 continue;
1392 }
1393 $filepath = $file["path"];
1394 $oldname = $file["name"];
1395
1396 $newname = str_replace($srcbasename, $destbasename, $oldname);
1397 if (!empty($newname) && $newname !== $oldname) {
1398 if ($file["type"] == "dir") {
1399 $res = dol_move_dir($filepath.'/'.$oldname, $filepath.'/'.$newname, $overwriteifexists, $indexdatabase, $renamedircontent);
1400 } else {
1401 $moreinfo = array('gen_or_uploaded' => 'unknown');
1402 $res = dol_move($filepath.'/'.$oldname, $filepath.'/'.$newname, '0', $overwriteifexists, 0, $indexdatabase, $moreinfo);
1403 }
1404 if (!$res) {
1405 return $result;
1406 }
1407 }
1408 }
1409 $result = true;
1410 }
1411 }
1412 }
1413 }
1414 return $result;
1415}
1416
1424function dol_unescapefile($filename)
1425{
1426 // Remove path information and dots around the filename, to prevent uploading
1427 // into different directories or replacing hidden system files.
1428 // Also remove control characters and spaces (\x00..\x20) around the filename:
1429 return trim(basename($filename), ".\x00..\x20");
1430}
1431
1432
1440function dolCheckVirus($src_file, $dest_file = '')
1441{
1442 global $db;
1443
1444 $reterrors = dolCheckOnFileName($src_file, $dest_file);
1445 if (!empty($reterrors)) {
1446 return $reterrors;
1447 }
1448
1449 if (getDolGlobalString('MAIN_ANTIVIRUS_UPLOAD_ON')) {
1450 if (!class_exists('AntiVir')) {
1451 require_once DOL_DOCUMENT_ROOT.'/core/class/antivir.class.php';
1452 }
1453 $antivir = new AntiVir($db);
1454 $result = $antivir->dol_avscan_file($src_file);
1455 if ($result < 0) { // If virus or error, we stop here
1456 $reterrors = $antivir->errors;
1457 return $reterrors;
1458 }
1459 }
1460 return array();
1461}
1462
1470function dolCheckOnFileName($src_file, $dest_file = '')
1471{
1472 if (preg_match('/\.pdf$/i', $dest_file)) {
1473 if (!getDolGlobalString('MAIN_ANTIVIRUS_ALLOW_JS_IN_PDF')) {
1474 dol_syslog("dolCheckOnFileName Check that pdf does not contains js code");
1475
1476 $tmp = file_get_contents(trim($src_file));
1477 if (preg_match('/[\n\s]+\/JavaScript[\n\s]+/m', $tmp)) {
1478 return array('ErrorFileIsAnInfectedPDFWithJSInside' => 'File is a PDF with javascript inside');
1479 }
1480 } else {
1481 dol_syslog("dolCheckOnFileName Check js into pdf disabled");
1482 }
1483 }
1484
1485 return array();
1486}
1487
1488
1510function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan = 0, $uploaderrorcode = 0, $nohook = 0, $keyforsourcefile = 'addedfile', $upload_dir = '', $mode = 0)
1511{
1512 global $conf;
1513 global $object, $hookmanager;
1514
1515 $reshook = 0;
1516 $file_name = $dest_file;
1517 $successcode = 1;
1518
1519 if (empty($nohook)) {
1520 $reshook = $hookmanager->initHooks(array('fileslib'));
1521
1522 $parameters = array('dest_file' => $dest_file, 'src_file' => $src_file, 'file_name' => $file_name, 'varfiles' => $keyforsourcefile, 'allowoverwrite' => $allowoverwrite);
1523 $reshook = $hookmanager->executeHooks('moveUploadedFile', $parameters, $object);
1524 }
1525
1526 if (empty($reshook)) {
1527 // If an upload error has been reported
1528 if ($uploaderrorcode) {
1529 switch ($uploaderrorcode) {
1530 case UPLOAD_ERR_INI_SIZE: // 1
1531 return 'ErrorFileSizeTooLarge';
1532 case UPLOAD_ERR_FORM_SIZE: // 2 - Exceed the MAX_FILE_SIZE specified into a field in form
1533 return 'ErrorFileSizeTooLarge';
1534 case UPLOAD_ERR_PARTIAL: // 3
1535 return 'ErrorPartialFile';
1536 case UPLOAD_ERR_NO_TMP_DIR: //
1537 return 'ErrorNoTmpDir';
1538 case UPLOAD_ERR_CANT_WRITE:
1539 return 'ErrorFailedToWriteInDir';
1540 case UPLOAD_ERR_EXTENSION:
1541 return 'ErrorUploadBlockedByAddon';
1542 default:
1543 break;
1544 }
1545 }
1546
1547 // Security:
1548 // If we need to make a virus scan
1549 if (empty($disablevirusscan) && file_exists($src_file)) {
1550 $checkvirusarray = dolCheckVirus($src_file, $dest_file);
1551 if (count($checkvirusarray)) {
1552 dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$dest_file.'") KO with antivirus: errors='.implode(',', $checkvirusarray), LOG_WARNING);
1553 // We return a translation key alone, so the caller can translate it. The technical message of the
1554 // antivirus is not appended to it (that would break the translation), it is kept into the log only.
1555 foreach (array_keys($checkvirusarray) as $errorkey) {
1556 if (is_string($errorkey)) { // A check that knows its own error key returns it as array key
1557 return $errorkey;
1558 }
1559 }
1560 return 'ErrorFileIsInfectedWithAVirus';
1561 }
1562 }
1563
1564 // Security:
1565 // Disallow file with some extensions. We rename them.
1566 // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code.
1567 if (isAFileWithExecutableContent($dest_file) && !getDolGlobalString('MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED')) {
1568 // $upload_dir ends with a slash, so be must be sure the medias dir to compare to ends with slash too.
1569 $publicmediasdirwithslash = $conf->medias->multidir_output[$conf->entity];
1570 if (!preg_match('/\/$/', $publicmediasdirwithslash)) {
1571 $publicmediasdirwithslash .= '/';
1572 }
1573
1574 if (strpos($upload_dir, $publicmediasdirwithslash) !== 0 || !getDolGlobalInt("MAIN_DOCUMENT_DISABLE_NOEXE_IN_MEDIAS_DIR")) { // We never add .noexe on files into media directory
1575 $file_name .= '.noexe';
1576 $successcode = 2;
1577 }
1578 }
1579
1580 // Security:
1581 // We refuse cache files/dirs, upload using .. and pipes into filenames.
1582 if (preg_match('/^\./', basename($src_file)) || preg_match('/\.\./', $src_file) || preg_match('/[<>|]/', $src_file)) {
1583 dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING);
1584 return -1;
1585 }
1586
1587 // Security:
1588 // We refuse cache files/dirs, upload using .. and pipes into filenames.
1589 if (preg_match('/^\./', basename($dest_file)) || preg_match('/\.\./', $dest_file) || preg_match('/[<>|]/', $dest_file)) {
1590 dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING);
1591 return -2;
1592 }
1593 }
1594
1595 if ($reshook < 0) { // At least one blocking error returned by one hook
1596 $errmsg = implode(',', $hookmanager->errors);
1597 if (empty($errmsg)) {
1598 $errmsg = 'ErrorReturnedBySomeHooks'; // Should not occurs. Added if hook is bugged and does not set ->errors when there is error.
1599 }
1600 return $errmsg;
1601 } elseif (empty($reshook)) {
1602 // The file functions must be in OS filesystem encoding.
1603 $src_file_osencoded = dol_osencode($src_file);
1604 $file_name_osencoded = dol_osencode($file_name);
1605
1606 // Check if destination dir is writable
1607 if (!is_writable(dirname($file_name_osencoded))) {
1608 dol_syslog("Files.lib::dol_move_uploaded_file Dir ".dirname($file_name_osencoded)." is not writable. Return 'ErrorDirNotWritable'", LOG_WARNING);
1609 return 'ErrorDirNotWritable';
1610 }
1611
1612 // Check if destination file already exists
1613 if (!$allowoverwrite) {
1614 if (file_exists($file_name_osencoded)) {
1615 dol_syslog("Files.lib::dol_move_uploaded_file File ".$file_name." already exists. Return 'ErrorFileAlreadyExists'", LOG_WARNING);
1616 return 'ErrorFileAlreadyExists';
1617 }
1618 } else { // We are allowed to erase
1619 if (is_dir($file_name_osencoded)) { // If there is a directory with name of file to create
1620 dol_syslog("Files.lib::dol_move_uploaded_file A directory with name ".$file_name." already exists. Return 'ErrorDirWithFileNameAlreadyExists'", LOG_WARNING);
1621 return 'ErrorDirWithFileNameAlreadyExists';
1622 }
1623 }
1624
1625 // Move file using a simple system function
1626 if ($mode == 0) {
1627 $return = move_uploaded_file($src_file_osencoded, $file_name_osencoded);
1628 } else {
1629 $return = rename($src_file_osencoded, $file_name_osencoded);
1630 }
1631
1632 if ($return) {
1633 dolChmod($file_name_osencoded);
1634 dol_syslog("Files.lib::dol_move_uploaded_file Success to move ".$src_file." to ".$file_name." - Umask=" . getDolGlobalString('MAIN_UMASK'), LOG_DEBUG);
1635 return $successcode; // Success
1636 } else {
1637 dol_syslog("Files.lib::dol_move_uploaded_file Failed to move ".$src_file." to ".$file_name, LOG_ERR);
1638 return -3; // Unknown error
1639 }
1640 }
1641
1642 return $successcode; // Success
1643}
1644
1660function dol_delete_file($file, $disableglob = 0, $nophperrors = 0, $nohook = 0, $object = null, $allowdotdot = false, $indexdatabase = 1, $nolog = 0)
1661{
1662 global $db, $user;
1663 global $hookmanager;
1664
1665 if (empty($nolog)) {
1666 dol_syslog("dol_delete_file file=".$file." disableglob=".$disableglob." nophperrors=".$nophperrors." nohook=".$nohook);
1667 }
1668
1669 // Security:
1670 // We refuse transversal using .. and pipes into filenames.
1671 if ((!$allowdotdot && preg_match('/\.\./', $file)) || preg_match('/[<>|]/', $file)) {
1672 dol_syslog("Refused to delete file ".$file, LOG_WARNING);
1673 return false;
1674 }
1675
1676 $reshook = 0;
1677 if (empty($nohook) && !empty($hookmanager)) {
1678 $hookmanager->initHooks(array('fileslib'));
1679
1680 $parameters = array(
1681 'file' => $file,
1682 'disableglob' => $disableglob,
1683 'nophperrors' => $nophperrors
1684 );
1685 $reshook = $hookmanager->executeHooks('deleteFile', $parameters, $object);
1686 }
1687
1688 if (empty($nohook) && $reshook != 0) { // reshook = 0 to do standard actions, 1 = ok and replace, -1 = ko
1689 dol_syslog("reshook=".$reshook);
1690 if ($reshook < 0) {
1691 return false;
1692 }
1693 return true;
1694 } else {
1695 $file_osencoded = dol_osencode($file); // New filename encoded in OS filesystem encoding charset
1696 if (empty($disableglob) && !empty($file_osencoded)) {
1697 $ok = true;
1698 $globencoded = str_replace('[', '\[', $file_osencoded);
1699 $globencoded = str_replace(']', '\]', $globencoded);
1700 $listoffiles = glob($globencoded); // This scan dir for files. If file does not exists, return empty.
1701
1702 if (!empty($listoffiles) && is_array($listoffiles)) {
1703 foreach ($listoffiles as $filename) {
1704 if ($nophperrors) {
1705 $ok = @unlink($filename);
1706 } else {
1707 $ok = unlink($filename);
1708 }
1709
1710 // If it fails and it is because of the missing write permission on parent dir
1711 if (!$ok && file_exists(dirname($filename)) && !(fileperms(dirname($filename)) & 0200)) {
1712 dol_syslog("Error in deletion, but parent directory exists with no permission to write, we try to change permission on parent directory and retry...", LOG_DEBUG);
1713 dolChmod(dirname($filename), decoct(fileperms(dirname($filename)) | 0200));
1714 // Now we retry deletion
1715 if ($nophperrors) {
1716 $ok = @unlink($filename);
1717 } else {
1718 $ok = unlink($filename);
1719 }
1720 }
1721
1722 if ($ok) {
1723 if (empty($nolog)) {
1724 dol_syslog("Removed file ".$filename, LOG_DEBUG);
1725 }
1726
1727 // Delete entry into ecm database
1728 $rel_filetodelete = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filename);
1729 if (!preg_match('/(\/temp\/|\/thumbs\/|\.meta$)/', $rel_filetodelete)) { // If not a tmp file
1730 if (is_object($db) && $indexdatabase) { // $db may not be defined when lib is in a context with define('NOREQUIREDB',1)
1731 $rel_filetodelete = preg_replace('/^[\\/]/', '', $rel_filetodelete);
1732 $rel_filetodelete = preg_replace('/\.noexe$/', '', $rel_filetodelete);
1733
1734 dol_syslog("Try to remove also entries in database for full relative path = ".$rel_filetodelete, LOG_DEBUG);
1735 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1736 $ecmfile = new EcmFiles($db);
1737 $entity = (isset($object->entity) ? $object->entity : null);
1738 $result = $ecmfile->fetch(0, '', $rel_filetodelete, '', '', '', 0, $entity);
1739 if ($result >= 0 && $ecmfile->id > 0) {
1740 $result = $ecmfile->delete($user);
1741 }
1742 if ($result < 0) {
1743 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1744 }
1745 }
1746 }
1747 } else {
1748 dol_syslog("Failed to remove file ".$filename, LOG_WARNING);
1749 // TODO Failure to remove can be because file was already removed or because of permission
1750 // If error because it does not exists, we should return true, and we should return false if this is a permission problem
1751 }
1752 }
1753 } else {
1754 $ok = true; // nothing to delete when glob is on must return ok
1755 dol_syslog("No files to delete found", LOG_DEBUG);
1756 }
1757 } else {
1758 $ok = false;
1759 if ($nophperrors) {
1760 $ok = @unlink($file_osencoded);
1761 } else {
1762 $ok = unlink($file_osencoded);
1763 }
1764
1765 $filename = $file_osencoded;
1766
1767 // If it fails and it is because of the missing write permission on parent dir
1768 if (!$ok && file_exists(dirname($filename)) && !(fileperms(dirname($filename)) & 0200)) {
1769 dol_syslog("Error in deletion, but parent directory exists with no permission to write, we try to change permission on parent directory and retry...", LOG_DEBUG);
1770 dolChmod(dirname($filename), decoct(fileperms(dirname($filename)) | 0200));
1771 // Now we retry deletion
1772 if ($nophperrors) {
1773 $ok = @unlink($filename);
1774 } else {
1775 $ok = unlink($filename);
1776 }
1777 }
1778
1779 if ($ok) {
1780 if (empty($nolog)) {
1781 dol_syslog("Removed file ".$filename, LOG_DEBUG);
1782 }
1783
1784 // Delete entry into ecm database
1785 $rel_filetodelete = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filename);
1786 if (!preg_match('/(\/temp\/|\/thumbs\/|\.meta$)/', $rel_filetodelete)) { // If not a tmp file
1787 if (is_object($db) && $indexdatabase) { // $db may not be defined when lib is in a context with define('NOREQUIREDB',1)
1788 $rel_filetodelete = preg_replace('/^[\\/]/', '', $rel_filetodelete);
1789 $rel_filetodelete = preg_replace('/\.noexe$/', '', $rel_filetodelete);
1790
1791 dol_syslog("Try to remove also entries in database for full relative path = ".$rel_filetodelete, LOG_DEBUG);
1792 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1793 $ecmfile = new EcmFiles($db);
1794 $entity = (isset($object->entity) ? $object->entity : null);
1795 $result = $ecmfile->fetch(0, '', $rel_filetodelete, '', '', '', 0, $entity);
1796 if ($result >= 0 && $ecmfile->id > 0) {
1797 $result = $ecmfile->delete($user);
1798 }
1799 if ($result < 0) {
1800 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1801 }
1802 }
1803 }
1804 } else {
1805 dol_syslog("Failed to remove file ".$filename, LOG_WARNING);
1806 }
1807 }
1808
1809 return $ok;
1810 }
1811}
1812
1822function dol_delete_dir($dir, $nophperrors = 0)
1823{
1824 // Security:
1825 // We refuse transversal using .. and pipes into filenames.
1826 if (preg_match('/\.\./', $dir) || preg_match('/[<>|]/', $dir)) {
1827 dol_syslog("Refused to delete dir ".$dir.' (contains invalid char sequence)', LOG_WARNING);
1828 return false;
1829 }
1830
1831 $dir_osencoded = dol_osencode($dir);
1832 return ($nophperrors ? @rmdir($dir_osencoded) : rmdir($dir_osencoded));
1833}
1834
1848function dol_delete_dir_recursive($dir, $count = 0, $nophperrors = 0, $onlysub = 0, &$countdeleted = 0, $indexdatabase = 1, $nolog = 0, $level = 0)
1849{
1850 if (empty($nolog) || empty($level)) {
1851 dol_syslog("functions.lib:dol_delete_dir_recursive ".$dir, LOG_DEBUG);
1852 }
1853 if ($level > 1000) {
1854 dol_syslog("functions.lib:dol_delete_dir_recursive too many depth", LOG_WARNING);
1855 }
1856
1857 if (dol_is_dir($dir)) {
1858 $dir_osencoded = dol_osencode($dir);
1859 if ($handle = opendir("$dir_osencoded")) {
1860 while (false !== ($item = readdir($handle))) {
1861 if (!utf8_check($item)) {
1862 $item = mb_convert_encoding($item, 'UTF-8', 'ISO-8859-1'); // should be useless
1863 }
1864
1865 if ($item != "." && $item != "..") {
1866 if (is_dir(dol_osencode("$dir/$item")) && !is_link(dol_osencode("$dir/$item"))) {
1867 $count = dol_delete_dir_recursive("$dir/$item", $count, $nophperrors, 0, $countdeleted, $indexdatabase, $nolog, ($level + 1));
1868 } else {
1869 dolChmod(dol_osencode("$dir/$item")); // Try to set permission to write on file
1870 $result = dol_delete_file("$dir/$item", 1, $nophperrors, 0, null, false, $indexdatabase, $nolog);
1871 $count++;
1872 if ($result) {
1873 $countdeleted++;
1874 }
1875 //else print 'Error on '.$item."\n";
1876 }
1877 }
1878 }
1879 closedir($handle);
1880
1881 // Delete also the main directory
1882 if (empty($onlysub)) {
1883 $result = dol_delete_dir($dir, $nophperrors);
1884 $count++;
1885 if ($result) {
1886 $countdeleted++;
1887 }
1888 //else print 'Error on '.$dir."\n";
1889 }
1890 }
1891 }
1892
1893 return $count;
1894}
1895
1896
1906{
1907 global $langs, $conf;
1908
1909 // Define parent dir of elements
1910 $element = $object->element;
1911
1912 if ($object->element == 'order_supplier') {
1913 $dir = $conf->fournisseur->commande->dir_output;
1914 } elseif ($object->element == 'invoice_supplier') {
1915 $dir = $conf->fournisseur->facture->dir_output;
1916 } elseif ($object->element == 'project') {
1917 $dir = $conf->project->dir_output;
1918 } elseif ($object->element == 'shipping') {
1919 $dir = $conf->expedition->dir_output.'/sending';
1920 } elseif ($object->element == 'delivery') {
1921 $dir = $conf->expedition->dir_output.'/receipt';
1922 } elseif ($object->element == 'fichinter') {
1923 $dir = $conf->ficheinter->dir_output;
1924 } else {
1925 $dir = empty($conf->$element->dir_output) ? '' : $conf->$element->dir_output;
1926 }
1927
1928 if (empty($dir)) {
1929 $object->error = $langs->trans('ErrorObjectNoSupportedByFunction');
1930 return 0;
1931 }
1932
1933 $refsan = dol_sanitizeFileName($object->ref);
1934 $dir = $dir."/".$refsan;
1935 $filepreviewnew = $dir."/".$refsan.".pdf_preview.png";
1936 $filepreviewnewbis = $dir."/".$refsan.".pdf_preview-0.png";
1937 $filepreviewold = $dir."/".$refsan.".pdf.png";
1938
1939 // For new preview files
1940 if (file_exists($filepreviewnew) && is_writable($filepreviewnew)) {
1941 if (!dol_delete_file($filepreviewnew, 1)) {
1942 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewnew);
1943 return 0;
1944 }
1945 }
1946 if (file_exists($filepreviewnewbis) && is_writable($filepreviewnewbis)) {
1947 if (!dol_delete_file($filepreviewnewbis, 1)) {
1948 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewnewbis);
1949 return 0;
1950 }
1951 }
1952 // For old preview files
1953 if (file_exists($filepreviewold) && is_writable($filepreviewold)) {
1954 if (!dol_delete_file($filepreviewold, 1)) {
1955 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewold);
1956 return 0;
1957 }
1958 } else {
1959 $multiple = $filepreviewold.".";
1960 for ($i = 0; $i < 20; $i++) {
1961 $preview = $multiple.$i;
1962
1963 if (file_exists($preview) && is_writable($preview)) {
1964 if (!dol_delete_file($preview, 1)) {
1965 $object->error = $langs->trans("ErrorFailedToOpenFile", $preview);
1966 return 0;
1967 }
1968 }
1969 }
1970 }
1971
1972 return 1;
1973}
1974
1984{
1985 global $conf;
1986
1987 // Create meta file
1988 if (!getDolGlobalString('MAIN_DOC_CREATE_METAFILE')) {
1989 return 0; // By default, no metafile.
1990 }
1991
1992 // Define parent dir of elements
1993 $element = $object->element;
1994
1995 if ($object->element == 'order_supplier') {
1996 $dir = $conf->fournisseur->dir_output.'/commande';
1997 } elseif ($object->element == 'invoice_supplier') {
1998 $dir = $conf->fournisseur->dir_output.'/facture';
1999 } elseif ($object->element == 'project') {
2000 $dir = $conf->project->dir_output;
2001 } elseif ($object->element == 'shipping') {
2002 $dir = $conf->expedition->dir_output.'/sending';
2003 } elseif ($object->element == 'delivery') {
2004 $dir = $conf->expedition->dir_output.'/receipt';
2005 } elseif ($object->element == 'fichinter') {
2006 $dir = $conf->ficheinter->dir_output;
2007 } else {
2008 $dir = empty($conf->$element->dir_output) ? '' : $conf->$element->dir_output;
2009 }
2010
2011 if ($dir) {
2012 $object->fetch_thirdparty();
2013
2014 $objectref = dol_sanitizeFileName((string) $object->ref);
2015 $dir = $dir."/".$objectref;
2016 $file = $dir."/".$objectref.".meta";
2017
2018 if (!is_dir($dir)) {
2019 dol_mkdir($dir);
2020 }
2021
2022 $meta = '';
2023 if (is_dir($dir)) {
2024 if (is_countable($object->lines) && count($object->lines) > 0) {
2025 $nblines = count($object->lines);
2026 } else {
2027 $nblines = 0;
2028 }
2029 $client = $object->thirdparty->name." ".$object->thirdparty->address." ".$object->thirdparty->zip." ".$object->thirdparty->town;
2030 $meta = "REFERENCE=\"".$object->ref."\"
2031 DATE=\"" . dol_print_date($object->date, '')."\"
2032 NB_ITEMS=\"" . $nblines."\"
2033 CLIENT=\"" . $client."\"
2034 AMOUNT_EXCL_TAX=\"" . $object->total_ht."\"
2035 AMOUNT=\"" . $object->total_ttc."\"\n";
2036
2037 for ($i = 0; $i < $nblines; $i++) {
2038 //For the items
2039 $meta .= "ITEM_".$i."_QUANTITY=\"".$object->lines[$i]->qty."\"
2040 ITEM_" . $i."_AMOUNT_WO_TAX=\"".$object->lines[$i]->total_ht."\"
2041 ITEM_" . $i."_VAT=\"".$object->lines[$i]->tva_tx."\"
2042 ITEM_" . $i."_DESCRIPTION=\"".str_replace("\r\n", "", nl2br($object->lines[$i]->desc))."\"
2043 ";
2044 }
2045 }
2046
2047 $fp = fopen($file, "w");
2048 fwrite($fp, $meta);
2049 fclose($fp);
2050
2051 dolChmod($file);
2052
2053 return 1;
2054 } else {
2055 dol_syslog('FailedToDetectDirInDolMetaCreateFor'.$object->element, LOG_WARNING);
2056 }
2057
2058 return 0;
2059}
2060
2061
2062
2071function dol_init_file_process($pathtoscan = '', $trackid = '')
2072{
2073 $listofpaths = array();
2074 $listofnames = array();
2075 $listofmimes = array();
2076
2077 if ($pathtoscan) {
2078 $listoffiles = dol_dir_list($pathtoscan, 'files');
2079 foreach ($listoffiles as $key => $val) {
2080 $listofpaths[] = $val['fullname'];
2081 $listofnames[] = $val['name'];
2082 $listofmimes[] = dol_mimetype($val['name']);
2083 }
2084 }
2085 $keytoavoidconflict = empty($trackid) ? '' : '-'.$trackid;
2086 $_SESSION["listofpaths".$keytoavoidconflict] = implode(';', $listofpaths);
2087 $_SESSION["listofnames".$keytoavoidconflict] = implode(';', $listofnames);
2088 $_SESSION["listofmimes".$keytoavoidconflict] = implode(';', $listofmimes);
2089}
2090
2091
2112function dol_add_file_process($upload_dir, $allowoverwrite = 0, $updatesessionordb = 0, $keyforsourcefile = 'addedfile', $savingdocmask = '', $link = null, $trackid = '', $generatethumbs = 1, $object = null, $forceFullTextIndexation = '', $mode = 0)
2113{
2114 global $db, $user, $conf, $langs;
2115
2116 $res = 0;
2117
2118 // If mode 1, prepare environment to be compatible with mode 0
2119 if ($mode == 1) {
2120 $_FILES = array($keyforsourcefile => array());
2121 $_FILES[$keyforsourcefile]['tmp_name'] = $keyforsourcefile;
2122 $_FILES[$keyforsourcefile]['name'] = $keyforsourcefile;
2123 $mode = 0;
2124 }
2125
2126 if (!empty($_FILES[$keyforsourcefile])) { // For view $_FILES[$keyforsourcefile]['error']
2127 dol_syslog('dol_add_file_process varfiles = '.$keyforsourcefile.' upload_dir='.$upload_dir.' allowoverwrite='.$allowoverwrite.' updatesessionordb='.$updatesessionordb.' savingdocmask='.$savingdocmask, LOG_DEBUG);
2128 $maxfilesinform = getDolGlobalInt("MAIN_SECURITY_MAX_ATTACHMENT_ON_FORMS", 10);
2129 if (is_array($_FILES[$keyforsourcefile]["name"]) && count($_FILES[$keyforsourcefile]["name"]) > $maxfilesinform) {
2130 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2131 setEventMessages($langs->trans("ErrorTooMuchFileInForm", $maxfilesinform), null, "errors");
2132 return -1;
2133 }
2134
2135 $result = dol_mkdir($upload_dir);
2136 //var_dump($result);exit;
2137
2138 if ($result >= 0) {
2139 $TFile = $_FILES[$keyforsourcefile];
2140 // Convert value of $TFile
2141 if (!is_array($TFile['name'])) {
2142 foreach ($TFile as $key => &$val) {
2143 $val = array($val);
2144 }
2145 }
2146
2147 $nbfile = count($TFile['name']);
2148 $nbok = 0;
2149 for ($i = 0; $i < $nbfile; $i++) {
2150 if (empty($TFile['name'][$i])) {
2151 continue; // For example, when submitting a form with no file name
2152 }
2153
2154 // Define $destfull (path to file including filename) and $destfile (only filename)
2155 $destfile = trim($TFile['name'][$i]);
2156 $destfull = $upload_dir."/".$destfile;
2157 $destfilewithoutext = preg_replace('/\.[^\.]+$/', '', $destfile);
2158
2159 if ($savingdocmask && strpos($savingdocmask, $destfilewithoutext) !== 0) {
2160 $destfile = trim(preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask));
2161 $destfull = $upload_dir."/".$destfile;
2162 }
2163
2164 $filenameto = basename($destfile);
2165 if (preg_match('/^\./', $filenameto)) {
2166 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2167 setEventMessages($langs->trans("ErrorFilenameCantStartWithDot", $filenameto), null, 'errors');
2168 break;
2169 }
2170 // dol_sanitizeFileName the file name and lowercase extension
2171 $info = pathinfo($destfull);
2172 $destfull = $info['dirname'].'/'.dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : ''));
2173 $info = pathinfo($destfile);
2174 $destfile = dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : ''));
2175
2176 // Check extension is allowed for upload.
2177 $defaultexecutableextensions = function_exists('getExecutableContent') ? implode(',', getExecutableContent()) : 'htm,html,shtml,js,phar,php,php3,php4,php5,phtml,pht,pl,py,cgi,ksh,sh,bash,bat,cmd,wpk,exe';
2178 $fileextensionrestriction = getDolGlobalString("MAIN_FILE_EXTENSION_UPLOAD_RESTRICTION", $defaultexecutableextensions);
2179 if (!empty($fileextensionrestriction)) {
2180 $arrayofregexextension = explode(",", $fileextensionrestriction);
2181
2182 foreach ($arrayofregexextension as $fileextension) {
2183 if (preg_match('/\.'.preg_quote(trim($fileextension), '/').'$/i', $destfull)) {
2184 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2185 setEventMessages($langs->trans("ErrorFilenameExtensionNotAllowed", $filenameto), null, 'errors');
2186 return -1;
2187 }
2188 }
2189 }
2190
2191 // We apply dol_string_nohtmltag also to clean file names (this remove duplicate spaces) because
2192 // this function is also applied when we rename and when we make try to download file (by the GETPOST(filename, 'alphanohtml') call).
2193 $destfile = dol_string_nohtmltag($destfile);
2194 $destfull = dol_string_nohtmltag($destfull);
2195
2196 // Check that filename is not the one of a reserved allowed CLI command
2197 global $dolibarr_main_restrict_os_commands;
2198 if (!empty($dolibarr_main_restrict_os_commands)) {
2199 $arrayofallowedcommand = explode(',', $dolibarr_main_restrict_os_commands);
2200 $arrayofallowedcommand = array_map('trim', $arrayofallowedcommand);
2201 if (in_array($destfile, $arrayofallowedcommand)) {
2202 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2203 setEventMessages($langs->trans("ErrorFilenameReserved", $destfile), null, 'errors');
2204 return -1;
2205 }
2206 }
2207
2208 // Move file from source directory to final destination. Check for virus is also embedded and a .noexe may also be appended on file name.
2209 $resupload = dol_move_uploaded_file($TFile['tmp_name'][$i], $destfull, $allowoverwrite, 0, $TFile['error'][$i], 0, $keyforsourcefile, $upload_dir, $mode);
2210
2211 if (is_numeric($resupload) && $resupload > 0) { // $resupload can be 'ErrorFileAlreadyExists', 'ErrorFileIsInfectedWithAVirus'
2212 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2213
2214 $tmparraysize = getDefaultImageSizes();
2215 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
2216 $maxheightsmall = $tmparraysize['maxheightsmall'];
2217 $maxwidthmini = $tmparraysize['maxwidthmini'];
2218 $maxheightmini = $tmparraysize['maxheightmini'];
2219 //$quality = $tmparraysize['quality'];
2220 $quality = 50; // For thumbs, we force quality to 50
2221
2222 // Generate thumbs.
2223 if ($generatethumbs) {
2224 if (image_format_supported($destfull) == 1) {
2225 // Create thumbs
2226 // We can't use $object->addThumbs here because there is no $object known
2227
2228 // Used on logon for example
2229 $imgThumbSmall = vignette($destfull, $maxwidthsmall, $maxheightsmall, '_small', $quality, "thumbs");
2230 // Create mini thumbs for image (Ratio is near 16/9)
2231 // Used on menu or for setup page for example
2232 $imgThumbMini = vignette($destfull, $maxwidthmini, $maxheightmini, '_mini', $quality, "thumbs");
2233 }
2234 }
2235
2236 // Update session
2237 if (empty($updatesessionordb)) {
2238 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2239 $formmail = new FormMail($db);
2240 $formmail->trackid = $trackid;
2241 $formmail->add_attached_files($destfull, $destfile, $TFile['type'][$i]);
2242 }
2243
2244 // Update index table of files (llx_ecm_files)
2245 if ($updatesessionordb == 1) {
2246 $sharefile = 0;
2247 if ($TFile['type'][$i] == 'application/pdf' && strpos($_SERVER["REQUEST_URI"], 'product') !== false && getDolGlobalString('PRODUCT_ALLOW_EXTERNAL_DOWNLOAD')) {
2248 $sharefile = 1;
2249 }
2250
2251 // If we allow overwrite, we may need to also overwrite index, so we delete index first so insert can work
2252 if ($allowoverwrite) {
2253 deleteFilesIntoDatabaseIndex($upload_dir, basename($destfile).($resupload == 2 ? '.noexe' : ''), '', $object);
2254 }
2255
2256 $result = addFileIntoDatabaseIndex($upload_dir, basename($destfile).($resupload == 2 ? '.noexe' : ''), $TFile['name'][$i], 'uploaded', $sharefile, $object, $forceFullTextIndexation);
2257 if ($result < 0) {
2258 if ($allowoverwrite) {
2259 // Do not show error message. We can have an error due to DB_ERROR_RECORD_ALREADY_EXISTS
2260 } else {
2261 setEventMessages('WarningFailedToAddFileIntoDatabaseIndex', null, 'warnings');
2262 }
2263 }
2264 }
2265
2266 $nbok++;
2267 } else {
2268 $langs->load("errors");
2269 if (is_numeric($resupload) && $resupload < 0) { // Unknown error
2270 setEventMessages($langs->trans("ErrorFileNotUploaded"), null, 'errors');
2271 } else { // Known error, $resupload is a translation key
2272 setEventMessages($langs->trans($resupload), null, 'errors');
2273 }
2274 }
2275 }
2276 if ($nbok > 0) {
2277 $res = $nbok;
2278 setEventMessages($langs->trans("FileTransferComplete"), null, 'mesgs');
2279 }
2280 } else {
2281 setEventMessages($langs->trans("ErrorFailedToCreateDir", $upload_dir), null, 'errors');
2282 }
2283 } elseif ($link) {
2284 require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php';
2285 $linkObject = new Link($db);
2286 $linkObject->entity = $conf->entity;
2287 $linkObject->url = $link;
2288 $linkObject->objecttype = GETPOST('objecttype', 'alpha');
2289 $linkObject->objectid = GETPOSTINT('objectid');
2290 $linkObject->label = GETPOST('label', 'alpha');
2291 $res = $linkObject->create($user);
2292
2293 if ($res > 0) {
2294 setEventMessages($langs->trans("LinkComplete"), null, 'mesgs');
2295 } else {
2296 setEventMessages($langs->trans("ErrorFileNotLinked"), null, 'errors');
2297 }
2298 } else {
2299 $langs->load("errors");
2300 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("File")), null, 'errors');
2301 }
2302
2303 return $res;
2304}
2305
2306
2318function dol_remove_file_process($filenb, $donotupdatesession = 0, $donotdeletefile = 1, $trackid = '')
2319{
2320 global $db, $langs;
2321
2322 $keytodelete = $filenb;
2323 $keytodelete--;
2324
2325 $listofpaths = array();
2326 $listofnames = array();
2327 $listofmimes = array();
2328 $keytoavoidconflict = empty($trackid) ? '' : '-'.$trackid;
2329 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
2330 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
2331 }
2332 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
2333 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
2334 }
2335 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
2336 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
2337 }
2338
2339 if ($keytodelete >= 0) {
2340 $pathtodelete = $listofpaths[$keytodelete];
2341 $filetodelete = $listofnames[$keytodelete];
2342 if (empty($donotdeletefile)) {
2343 $result = dol_delete_file($pathtodelete, 1); // The delete of ecm database is inside the function dol_delete_file
2344 } else {
2345 $result = 0;
2346 }
2347 if ($result >= 0) {
2348 if (empty($donotdeletefile)) {
2349 $langs->load("other");
2350 setEventMessages($langs->trans("FileWasRemoved", $filetodelete), null, 'mesgs');
2351 }
2352 if (empty($donotupdatesession)) {
2353 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2354 $formmail = new FormMail($db);
2355 $formmail->trackid = $trackid;
2356 $formmail->remove_attached_files($keytodelete);
2357 }
2358 }
2359 }
2360}
2361
2362
2377function addFileIntoDatabaseIndex($dir, $file, $fullpathorig = '', $mode = 'uploaded', $setsharekey = 0, $object = null, $forceFullTextIndexation = '')
2378{
2379 global $db, $user;
2380
2381 $result = 0;
2382 $error = 0;
2383
2384 dol_syslog("addFileIntoDatabaseIndex dir=".$dir." file=".$file, LOG_DEBUG);
2385
2386 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
2387
2388 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a temporary directory. TODO Does this test work ?
2389 $filename = basename(preg_replace('/\.noexe$/', '', $file));
2390 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
2391 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
2392
2393 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
2394 $ecmfile = new EcmFiles($db);
2395 $ecmfile->filepath = $rel_dir;
2396 $ecmfile->filename = $filename;
2397 $ecmfile->label = md5_file(dol_osencode($dir.'/'.$file)); // MD5 of file content
2398 $ecmfile->fullpath_orig = $fullpathorig;
2399 $ecmfile->gen_or_uploaded = $mode;
2400 $ecmfile->description = ''; // indexed content
2401 $ecmfile->keywords = ''; // keyword content
2402
2403 if (is_object($object) && $object->id > 0) {
2404 $ecmfile->src_object_id = $object->id;
2405 if (isset($object->table_element)) {
2406 $ecmfile->src_object_type = $object->table_element;
2407 } else {
2408 dol_syslog('Error: object ' . get_class($object) . ' has no table_element attribute.');
2409 return -1;
2410 }
2411 if (isset($object->src_object_description)) {
2412 $ecmfile->description = $object->src_object_description;
2413 }
2414 if (isset($object->src_object_keywords)) {
2415 $ecmfile->keywords = $object->src_object_keywords;
2416 }
2417 if (isset($object->entity)) {
2418 $ecmfile->entity = $object->entity;
2419 }
2420 }
2421
2422 if (getDolGlobalString('MAIN_FORCE_SHARING_ON_ANY_UPLOADED_FILE')) {
2423 $setsharekey = 1;
2424 }
2425
2426 if ($setsharekey) {
2427 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
2428 $ecmfile->share = getRandomPassword(true);
2429 }
2430
2431 // Use a convert tool for Doc to Text
2432 $useFullTextIndexation = getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT'); // Can be '', 'pdftotext' or 'docling'
2433 if (empty($useFullTextIndexation) && $forceFullTextIndexation == '1') {
2434 if (getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_PDFTOTEXT')) { // Command line for pdftotext
2435 $useFullTextIndexation = 'pdftotext';
2436 } elseif (getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_DOCLING')) { // Command line for docling
2437 $useFullTextIndexation = 'docling';
2438 }
2439 }
2440
2441 //$useFullTextIndexation = 1;
2442 if ($useFullTextIndexation) {
2443 $ecmfile->filepath = $rel_dir;
2444 $ecmfile->filename = $filename;
2445
2446 $filetoprocess = $dir.'/'.$ecmfile->filename;
2447
2448 $textforfulltextindex = '';
2449 $keywords = '';
2450 $cmd = '';
2451 if (preg_match('/\.pdf/i', $filename)) {
2452 // Convertfile into text
2453 $result = dolDocToText($filetoprocess);
2454
2455 if (empty($result['error'])) {
2456 $textforfulltextindex = $result['content'];
2457 $filetoprocess = $result['keywords'];
2458 $cmd = $result['cmd'];
2459 } else {
2460 $error++;
2461 }
2462 }
2463
2464 if ($cmd) {
2465 $ecmfile->description = 'File content generated by '.$cmd;
2466 }
2467 $ecmfile->content = $textforfulltextindex;
2468 $ecmfile->keywords = $keywords;
2469 }
2470
2471 if (!$error) {
2472 $result = $ecmfile->create($user);
2473 if ($result < 0) {
2474 dol_syslog($ecmfile->error);
2475 }
2476 }
2477 }
2478
2479 return $result;
2480}
2481
2491function deleteFilesIntoDatabaseIndex($dir, $file, $mode = 'uploaded', $object = null)
2492{
2493 global $conf, $db;
2494
2495 $error = 0;
2496
2497 if (empty($dir)) {
2498 dol_syslog("deleteFilesIntoDatabaseIndex: dir parameter can't be empty", LOG_ERR);
2499 return -1;
2500 }
2501
2502 dol_syslog("deleteFilesIntoDatabaseIndex dir=".$dir." file=".$file, LOG_DEBUG);
2503
2504 $db->begin();
2505
2506 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
2507
2508 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a temporary directory. TODO Does this test work ?
2509 //$filename = basename($file);
2510 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
2511 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
2512
2513 if (!$error) {
2514 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'ecm_files';
2515 if (isset($object->entity)) {
2516 $sql .= ' WHERE entity = ' . ((int) $object->entity);
2517 } else {
2518 $sql .= ' WHERE entity = ' . ((int) $conf->entity);
2519 }
2520 $sql .= " AND filepath = '".$db->escape($rel_dir)."'";
2521 if ($file) {
2522 $sql .= " AND filename = '".$db->escape($file)."'";
2523 }
2524 if ($mode) {
2525 $sql .= " AND gen_or_uploaded = '".$db->escape($mode)."'";
2526 }
2527
2528 $resql = $db->query($sql);
2529 if (!$resql) {
2530 $error++;
2531 dol_syslog(__FUNCTION__.' '.$db->lasterror(), LOG_ERR);
2532 }
2533 }
2534 }
2535
2536 // Commit or rollback
2537 if ($error) {
2538 $db->rollback();
2539 return -1 * $error;
2540 } else {
2541 $db->commit();
2542 return 1;
2543 }
2544}
2545
2553function isRealPdf(string $filePath)
2554{
2555 if (!is_file($filePath) || !is_readable($filePath)) {
2556 return false;
2557 }
2558
2559 // Open file
2560 $handle = fopen($filePath, 'rb');
2561 if (!$handle) {
2562 return false;
2563 }
2564 $header = fread($handle, 5);
2565 fclose($handle);
2566
2567 if ($header !== '%PDF-') {
2568 return false;
2569 }
2570
2571 // Check using finfo_file
2572 /*
2573 $finfo = finfo_open(FILEINFO_MIME_TYPE);
2574 $mime = finfo_file($finfo, $filePath);
2575 finfo_close($finfo);
2576 if ($mime !== 'application/pdf') {
2577 return false;
2578 }
2579 */
2580
2581 return true;
2582}
2583
2595function dol_convert_file($fileinput, $ext = 'png', $fileoutput = '', $page = '')
2596{
2597 if (class_exists('Imagick')) {
2598 $image = new Imagick();
2599 try {
2600 // Imagick may have a support for Magick Scripting Language (MSL) that allows to run execution code with some files like SVG. So we need to check
2601 // that file is really a PDF file.
2602 // Note: The Imagick policy options can be disabled into /etc/ImageMagick*/policy.xml.
2603 if (!isRealPdf($fileinput)) {
2604 dol_syslog("We try to convert a PDF file with name ".$fileinput." but it is not a real PDF file (hack attempt ?).", LOG_WARNING);
2605 return -4;
2606 }
2607
2608 $filetoconvert = $fileinput.(($page != '') ? '['.$page.']' : '');
2609 //var_dump($filetoconvert);
2610 $ret = $image->readImage($filetoconvert);
2611 } catch (Exception $e) {
2612 $ext = pathinfo($fileinput, PATHINFO_EXTENSION);
2613 dol_syslog("Failed to read image using Imagick (Try to install package 'apt-get install php-imagick ghostscript' and check there is no policy to disable ".$ext." conversion in /etc/ImageMagick*/policy.xml): ".$e->getMessage(), LOG_WARNING);
2614 return 0;
2615 }
2616
2617 if ($ret) {
2618 $ret = $image->setImageFormat($ext);
2619 if ($ret) {
2620 if (empty($fileoutput)) {
2621 $fileoutput = $fileinput.".".$ext;
2622 }
2623
2624 $count = $image->getNumberImages();
2625
2626 if (!dol_is_file($fileoutput) || is_writable($fileoutput)) {
2627 try {
2628 $ret = $image->writeImages($fileoutput, true);
2629 } catch (Exception $e) {
2630 dol_syslog($e->getMessage(), LOG_WARNING);
2631 }
2632 } else {
2633 dol_syslog("Warning: Failed to write cache preview file '.$fileoutput.'. Check permission on file/dir", LOG_ERR);
2634 }
2635 if ($ret) {
2636 return $count;
2637 } else {
2638 return -3;
2639 }
2640 } else {
2641 return -2;
2642 }
2643 } else {
2644 return -1;
2645 }
2646 } else {
2647 return 0;
2648 }
2649}
2650
2651
2663function dol_compress_file($inputfile, $outputfile, $mode = "gz", &$errorstring = null)
2664{
2665 $foundhandler = 0;
2666 //var_dump(basename($inputfile)); exit;
2667
2668 try {
2669 dol_syslog("dol_compress_file mode=".$mode." inputfile=".$inputfile." outputfile=".$outputfile);
2670
2671 $data = implode("", file(dol_osencode($inputfile)));
2672 $compressdata = null;
2673 if ($mode == 'gz' && function_exists('gzencode')) {
2674 $foundhandler = 1;
2675 $compressdata = gzencode($data, 9);
2676 } elseif ($mode == 'bz' && function_exists('bzcompress')) {
2677 $foundhandler = 1;
2678 $compressdata = bzcompress($data, 9);
2679 } elseif ($mode == 'zstd' && function_exists('zstd_compress')) {
2680 $foundhandler = 1;
2681 $compressdata = zstd_compress($data, 9);
2682 } elseif ($mode == 'zip') {
2683 if (class_exists('ZipArchive') && getDolGlobalString('MAIN_USE_ZIPARCHIVE_FOR_ZIP_COMPRESS')) {
2684 $foundhandler = 1;
2685
2686 $rootPath = realpath($inputfile);
2687
2688 dol_syslog("Class ZipArchive is set so we zip using ZipArchive to zip into ".$outputfile.' rootPath='.$rootPath);
2689 $zip = new ZipArchive();
2690
2691 if ($zip->open($outputfile, ZipArchive::CREATE) !== true) {
2692 $errorstring = "dol_compress_file failure - Failed to open file ".$outputfile."\n";
2693 dol_syslog($errorstring, LOG_ERR);
2694
2695 global $errormsg;
2696 $errormsg = $errorstring;
2697
2698 return -6;
2699 }
2700
2701 // Create recursive directory iterator
2703 $files = new RecursiveIteratorIterator(
2704 new RecursiveDirectoryIterator($rootPath, FilesystemIterator::UNIX_PATHS),
2705 RecursiveIteratorIterator::LEAVES_ONLY
2706 );
2707 '@phan-var-force SplFileInfo[] $files';
2708
2709 foreach ($files as $name => $file) {
2710 // Skip directories (they would be added automatically)
2711 if (!$file->isDir()) {
2712 // Get real and relative path for current file
2713 $filePath = $file->getPath(); // the full path with filename using the $inputdir root.
2714 $fileName = $file->getFilename();
2715 $fileFullRealPath = $file->getRealPath(); // the full path with name and transformed to use real path directory.
2716
2717 //$relativePath = dol_substr($fileFullRealPath, strlen($rootPath) + 1);
2718 $relativePath = substr(($filePath ? $filePath.'/' : '').$fileName, strlen($rootPath) + 1);
2719
2720 // Add current file to archive
2721 $zip->addFile($fileFullRealPath, $relativePath);
2722 }
2723 }
2724
2725 // Zip archive will be created only after closing object
2726 $zip->close();
2727
2728 dol_syslog("dol_compress_file success - ".$zip->numFiles." files");
2729 return 1;
2730 }
2731
2732 if (defined('ODTPHP_PATHTOPCLZIP')) {
2733 $foundhandler = 1;
2734
2735 include_once ODTPHP_PATHTOPCLZIP.'pclzip.lib.php';
2736 $archive = new PclZip($outputfile);
2737
2738 $result = $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
2739
2740 if ($result === 0) {
2741 global $errormsg;
2742 $errormsg = $archive->errorInfo(true);
2743
2744 if ($archive->errorCode() == PCLZIP_ERR_WRITE_OPEN_FAIL) {
2745 $errorstring = "PCLZIP_ERR_WRITE_OPEN_FAIL";
2746 dol_syslog("dol_compress_file error - archive->errorCode() = PCLZIP_ERR_WRITE_OPEN_FAIL", LOG_ERR);
2747 return -4;
2748 }
2749
2750 $errorstring = "dol_compress_file error archive->errorCode = ".$archive->errorCode()." errormsg=".$errormsg;
2751 dol_syslog("dol_compress_file failure - ".$errormsg, LOG_ERR);
2752 return -3;
2753 } else {
2754 dol_syslog("dol_compress_file success - ".count($result)." files");
2755 return 1;
2756 }
2757 }
2758 }
2759
2760 if ($foundhandler && is_string($compressdata)) {
2761 $fp = fopen($outputfile, "w");
2762 fwrite($fp, $compressdata);
2763 fclose($fp);
2764 return 1;
2765 } else {
2766 $errorstring = "Try to zip with format ".$mode." with no handler for this format";
2767 dol_syslog($errorstring, LOG_ERR);
2768
2769 global $errormsg;
2770 $errormsg = $errorstring;
2771 return -2;
2772 }
2773 } catch (Exception $e) {
2774 global $langs, $errormsg;
2775 $langs->load("errors");
2776 $errormsg = $langs->trans("ErrorFailedToWriteInDir");
2777
2778 $errorstring = "Failed to open file ".$outputfile;
2779 dol_syslog($errorstring, LOG_ERR);
2780 return -1;
2781 }
2782}
2783
2792function dol_uncompress($inputfile, $outputdir)
2793{
2794 global $langs, $db;
2795
2796 $fileinfo = pathinfo($inputfile);
2797 $fileinfo["extension"] = strtolower($fileinfo["extension"]);
2798
2799 if ($fileinfo["extension"] == "zip") {
2800 if (defined('ODTPHP_PATHTOPCLZIP') && !getDolGlobalString('MAIN_USE_ZIPARCHIVE_FOR_ZIP_UNCOMPRESS')) {
2801 dol_syslog("Constant ODTPHP_PATHTOPCLZIP for pclzip library is set to ".ODTPHP_PATHTOPCLZIP.", so we use Pclzip to unzip into ".$outputdir);
2802 include_once ODTPHP_PATHTOPCLZIP.'pclzip.lib.php';
2803 $archive = new PclZip($inputfile);
2804
2805 // We create output dir manually, so it uses the correct permission (When created by the archive->extract, dir is rwx for everybody).
2806 dol_mkdir(dol_sanitizePathName($outputdir));
2807
2808 try {
2809 // Extract into outputdir, but only files that match the regex '/^((?!\.\.).)*$/' that means "does not include .."
2810 $result = $archive->extract(PCLZIP_OPT_PATH, $outputdir, PCLZIP_OPT_BY_PREG, '/^((?!\.\.).)*$/');
2811 } catch (Exception $e) {
2812 return array('error' => $e->getMessage());
2813 }
2814
2815 if (!is_array($result) && $result <= 0) {
2816 return array('error' => $archive->errorInfo(true));
2817 } else {
2818 $ok = 1;
2819 $errmsg = '';
2820 // Loop on each file to check result for unzipping file
2821 foreach ($result as $key => $val) {
2822 if ($val['status'] == 'path_creation_fail') {
2823 $langs->load("errors");
2824 $ok = 0;
2825 $errmsg = $langs->trans("ErrorFailToCreateDir", $val['filename']);
2826 break;
2827 }
2828 if ($val['status'] == 'write_protected') {
2829 $langs->load("errors");
2830 $ok = 0;
2831 $errmsg = $langs->trans("ErrorFailToCreateFile", $val['filename']);
2832 break;
2833 }
2834 }
2835
2836 if ($ok) {
2837 return array();
2838 } else {
2839 return array('error' => $errmsg);
2840 }
2841 }
2842 }
2843
2844 if (class_exists('ZipArchive')) { // Must install php-zip to have it
2845 dol_syslog("Class ZipArchive is set so we unzip using ZipArchive to unzip into ".$outputdir);
2846 $zip = new ZipArchive();
2847 $res = $zip->open($inputfile);
2848 if ($res === true) {
2849 //$zip->extractTo($outputdir.'/');
2850 // We must extract one file at time so we can check that file name does not contain '..' to avoid transversal path of zip built for example using
2851 // python3 path_traversal_archiver.py <Created_file_name> test.zip -l 10 -p tmp/
2852 // with -l is the range of dot to go back in path.
2853 // and path_traversal_archiver.py found at https://github.com/Alamot/code-snippets/blob/master/path_traversal/path_traversal_archiver.py
2854 for ($i = 0; $i < $zip->numFiles; $i++) {
2855 if (preg_match('/\.\./', $zip->getNameIndex($i))) {
2856 dol_syslog("Warning: Try to unzip a file with a transversal path ".$zip->getNameIndex($i), LOG_WARNING);
2857 continue; // Discard the file
2858 }
2859 $zip->extractTo($outputdir.'/', array($zip->getNameIndex($i)));
2860 }
2861
2862 $zip->close();
2863 return array();
2864 } else {
2865 return array('error' => 'ErrUnzipFails');
2866 }
2867 }
2868
2869 return array('error' => 'ErrNoZipEngine');
2870 } elseif (in_array($fileinfo["extension"], array('gz', 'bz2', 'zst'))) {
2871 include_once DOL_DOCUMENT_ROOT."/core/class/utils.class.php";
2872 $utils = new Utils($db);
2873
2874 dol_mkdir(dol_sanitizePathName($outputdir));
2875 $outputfilename = escapeshellcmd(dol_sanitizePathName($outputdir).'/'.dol_sanitizeFileName($fileinfo["filename"]));
2876 dol_delete_file($outputfilename.'.tmp');
2877 dol_delete_file($outputfilename.'.err');
2878
2879 $extension = strtolower(pathinfo($fileinfo["filename"], PATHINFO_EXTENSION));
2880 if ($extension == "tar") {
2881 $cmd = 'tar -C '.escapeshellcmd(dol_sanitizePathName($outputdir)).' -xvf '.escapeshellcmd(dol_sanitizePathName($fileinfo["dirname"]).'/'.dol_sanitizeFileName($fileinfo["basename"]));
2882
2883 $resarray = $utils->executeCLI($cmd, $outputfilename.'.tmp', 0, $outputfilename.'.err', 0);
2884 if ($resarray["result"] != 0) {
2885 $resarray["error"] .= file_get_contents($outputfilename.'.err');
2886 }
2887 } else {
2888 $program = "";
2889 if ($fileinfo["extension"] == "gz") {
2890 $program = 'gzip';
2891 } elseif ($fileinfo["extension"] == "bz2") {
2892 $program = 'bzip2';
2893 } elseif ($fileinfo["extension"] == "zst") {
2894 $program = 'zstd';
2895 } else {
2896 return array('error' => 'ErrorBadFileExtension');
2897 }
2898 $cmd = $program.' -dc '.escapeshellcmd(dol_sanitizePathName($fileinfo["dirname"]).'/'.dol_sanitizeFileName($fileinfo["basename"]));
2899 $cmd .= ' > '.$outputfilename;
2900
2901 $resarray = $utils->executeCLI($cmd, $outputfilename.'.tmp', 0, null, 1, $outputfilename.'.err');
2902 if ($resarray["result"] != 0) {
2903 $errfilecontent = @file_get_contents($outputfilename.'.err');
2904 if ($errfilecontent) {
2905 $resarray["error"] .= " - ".$errfilecontent;
2906 }
2907 }
2908 }
2909 return $resarray["result"] != 0 ? array('error' => $resarray["error"]) : array();
2910 }
2911
2912 return array('error' => 'ErrorBadFileExtension');
2913}
2914
2915
2928function dol_compress_dir($inputdir, $outputfile, $mode = "zip", $excludefiles = '', $rootdirinzip = '', $newmask = '0')
2929{
2930 $foundhandler = 0;
2931
2932 dol_syslog("Try to zip dir ".$inputdir." into ".$outputfile." mode=".$mode);
2933
2934 if (!dol_is_dir(dirname($outputfile)) || !is_writable(dirname($outputfile))) {
2935 global $langs, $errormsg;
2936 $langs->load("errors");
2937 $errormsg = $langs->trans("ErrorFailedToWriteInDir", $outputfile);
2938 return -3;
2939 }
2940
2941 try {
2942 if ($mode == 'gz') {
2943 $foundhandler = 0;
2944 } elseif ($mode == 'bz') {
2945 $foundhandler = 0;
2946 } elseif ($mode == 'zip') {
2947 /*if (defined('ODTPHP_PATHTOPCLZIP'))
2948 {
2949 $foundhandler=0; // TODO implement this
2950
2951 include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
2952 $archive = new PclZip($outputfile);
2953 $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
2954 //$archive->add($inputfile);
2955 return 1;
2956 }
2957 else*/
2958 //if (class_exists('ZipArchive') && !empty($conf->global->MAIN_USE_ZIPARCHIVE_FOR_ZIP_COMPRESS))
2959
2960 if (class_exists('ZipArchive')) {
2961 $foundhandler = 1;
2962
2963 // Initialize archive object
2964 $zip = new ZipArchive();
2965 $result = $zip->open($outputfile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
2966 if ($result !== true) {
2967 global $langs, $errormsg;
2968 $langs->load("errors");
2969 $errormsg = $langs->trans("ErrorFailedToBuildArchive", $outputfile);
2970 return -4;
2971 }
2972
2973 // Create recursive directory iterator
2974 // This does not return symbolic links
2976 $files = new RecursiveIteratorIterator(
2977 new RecursiveDirectoryIterator($inputdir, FilesystemIterator::UNIX_PATHS),
2978 RecursiveIteratorIterator::LEAVES_ONLY
2979 );
2980 '@phan-var-force SplFileInfo[] $files';
2981
2982 //var_dump($inputdir);
2983 foreach ($files as $name => $file) {
2984 // Skip directories (they would be added automatically)
2985 if (!$file->isDir()) {
2986 // Get real and relative path for current file
2987 $filePath = $file->getPath(); // the full path with filename using the $inputdir root.
2988 $fileName = $file->getFilename();
2989 $fileFullRealPath = $file->getRealPath(); // the full path with name and transformed to use real path directory.
2990
2991 //$relativePath = ($rootdirinzip ? $rootdirinzip.'/' : '').dol_substr($fileFullRealPath, strlen($inputdir) + 1);
2992 $relativePath = ($rootdirinzip ? $rootdirinzip.'/' : '').substr(($filePath ? $filePath.'/' : '').$fileName, strlen($inputdir) + 1);
2993
2994 //var_dump($filePath);var_dump($fileFullRealPath);var_dump($relativePath);
2995 if (empty($excludefiles) || !preg_match($excludefiles, $fileFullRealPath)) {
2996 // Add current file to archive
2997 $zip->addFile($fileFullRealPath, $relativePath);
2998 }
2999 }
3000 }
3001
3002 // Zip archive will be created only after closing object
3003 $zip->close();
3004
3005 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
3006 $newmask = getDolGlobalString('MAIN_UMASK');
3007 }
3008 if (empty($newmask)) { // This should no happen
3009 dol_syslog("Warning: dol_compress_dir called with empty value for newmask and no default value defined", LOG_WARNING);
3010 $newmask = '0664';
3011 }
3012
3013 dolChmod($outputfile, $newmask);
3014
3015 return 1;
3016 }
3017 }
3018
3019 if (!$foundhandler) {
3020 dol_syslog("Try to zip with format ".$mode." with no handler for this format", LOG_ERR);
3021 return -2;
3022 } else {
3023 return 0;
3024 }
3025 } catch (Exception $e) {
3026 global $langs, $errormsg;
3027 $langs->load("errors");
3028 dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
3029 dol_syslog($e->getMessage(), LOG_ERR);
3030 $errormsg = $langs->trans("ErrorFailedToBuildArchive", $outputfile).' - '.$e->getMessage();
3031 return -1;
3032 }
3033}
3034
3035
3036
3048function dol_most_recent_file($dir, $regexfilter = '', $excludefilter = array('(\.meta|_preview.*\.png)$', '^\.'), $nohook = 0, $mode = 0, $limit = 0)
3049{
3050 $tmparray = dol_dir_list($dir, 'files', 0, $regexfilter, $excludefilter, 'date', SORT_DESC, $mode, $nohook);
3051 if ($limit > 1) {
3052 return array_slice($tmparray, 0, $limit);
3053 }
3054 return isset($tmparray[0]) ? $tmparray[0] : null;
3055}
3056
3070function dol_check_secure_access_document($modulepart, $original_file, $entity, $fuser = null, $refname = '', $mode = 'read')
3071{
3072 global $conf, $db, $user, $hookmanager;
3073 global $dolibarr_main_data_root, $dolibarr_main_document_root_alt;
3074 global $object;
3075
3076 if (!is_object($fuser)) {
3077 $fuser = $user;
3078 }
3079
3080 if (empty($modulepart)) {
3081 return 'ErrorBadParameter';
3082 }
3083
3084 $originalmodulepart = $modulepart;
3085
3086 if (empty($entity)) {
3087 if (!isModEnabled('multicompany')) {
3088 $entity = 1;
3089 } else {
3090 $entity = 0;
3091 }
3092 } else {
3093 // TODO Test that the user in session of conf->entity can see objects of the target $entity like restrictedArea() does, however
3094 // it is better to limit the scope of this function to check "per module" permission only, and to do the test on "per object" permission
3095 // by calling restrictedArea()by the caller.
3096 }
3097
3098 // Fix modulepart for backward compatibility
3099 if ($modulepart == 'facture') {
3100 $modulepart = 'invoice';
3101 } elseif ($modulepart == 'users') {
3102 $modulepart = 'user';
3103 } elseif ($modulepart == 'tva') {
3104 $modulepart = 'tax-vat';
3105 } elseif ($modulepart == 'expedition' && strpos($original_file, 'receipt/') === 0) {
3106 // Fix modulepart delivery
3107 $modulepart = 'delivery';
3108 } elseif ($modulepart == 'propale') {
3109 $modulepart = 'propal';
3110 }
3111
3112 // If modulepart is composed of an objectpart@modulepart, we keep modulepart.
3113 $reg = array();
3114 if (preg_match('/(\w+)@(\w+)$/', $modulepart, $reg)) {
3115 $modulepart = $reg[2];
3116 }
3117
3118 //print 'dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity;
3119 dol_syslog('dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity);
3120
3121 // We define $accessallowed and $sqlprotectagainstexternals
3122 $accessallowed = 0;
3123 $sqlprotectagainstexternals = '';
3124 $ret = array();
3125
3126 // Find the subdirectory name as the reference. For example original_file='10/myfile.pdf' -> refname='10'
3127 if (empty($refname)) {
3128 $refname = basename(dirname($original_file)."/");
3129 if ($refname == 'thumbs' || $refname == 'temp') {
3130 // If we get the thumbs directory, we must go one step higher. For example original_file='10/thumbs/myfile_small.jpg' -> refname='10'
3131 $refname = basename(dirname(dirname($original_file))."/");
3132 }
3133 }
3134
3135 // Define possible keys to use for permission check
3136 $lire = 'lire';
3137 $read = 'read';
3138 $download = 'download';
3139 if ($mode == 'write') {
3140 $lire = 'creer';
3141 $read = 'write';
3142 $download = 'upload';
3143 } elseif ($mode == 'delete') {
3144 $lire = 'supprimer';
3145 $read = 'delete';
3146 $download = 'upload';
3147 }
3148
3149 // Wrapping for miscellaneous medias files
3150 if ($modulepart == 'common') {
3151 // Wrapping for some images
3152 $accessallowed = 1;
3153 $original_file = DOL_DOCUMENT_ROOT.'/public/theme/common/'.$original_file;
3154 } elseif ($modulepart == 'medias' && !empty($dolibarr_main_data_root)) {
3155 /* the medias directory is by default a public directory accessible online for everybody, so test on permission per entity is not done, it has no sense */
3156 if (empty($entity)) {
3157 $entity = 1;
3158 }
3159 $accessallowed = 0;
3160 if ($mode == 'write') {
3161 if ($fuser->hasRight('website', 'write')) {
3162 $accessallowed = 1;
3163 }
3164 } else {
3165 $accessallowed = 1; // As dir is public, we allow read access to all files in medias directory
3166 }
3167 $original_file = (empty($conf->medias->multidir_output[$entity]) ? (empty($conf->medias->dir_output) ? DOL_DATA_ROOT.'/medias' : $conf->medias->dir_output) : $conf->medias->multidir_output[$entity]).'/'.$original_file;
3168 } elseif ($modulepart == 'logs' && !empty($dolibarr_main_data_root)) {
3169 // Wrapping for *.log files, like when used with url http://.../document.php?modulepart=logs&file=dolibarr.log
3170 $accessallowed = ($user->admin && basename($original_file) == $original_file && preg_match('/^dolibarr.*\.(log|json)$/', basename($original_file)));
3171 $original_file = $dolibarr_main_data_root.'/'.$original_file;
3172 } elseif ($modulepart == 'doctemplates' && !empty($dolibarr_main_data_root)) {
3173 $accessallowed = $user->admin;
3174 $relative_file = $original_file;
3175 $ent = ($entity > 0 ? $entity : $conf->entity);
3176 $path_with_entity = $dolibarr_main_data_root . '/' . $ent . '/doctemplates/' . $relative_file;
3177 if ($ent > 1 && file_exists(dol_osencode($path_with_entity))) {
3178 $original_file = $path_with_entity;
3179 } else {
3180 $original_file = $dolibarr_main_data_root . '/doctemplates/' . $relative_file;
3181 }
3182 } elseif ($modulepart == 'doctemplateswebsite' && !empty($dolibarr_main_data_root)) {
3183 // Wrapping for doctemplates of websites
3184 $accessallowed = ($fuser->hasRight('website', 'write') && preg_match('/\.jpg$/i', basename($original_file)));
3185 $original_file = $dolibarr_main_data_root.'/doctemplates/websites/'.$original_file;
3186 } elseif ($modulepart == 'packages' && !empty($dolibarr_main_data_root)) { // To download zip of modules
3187 // Wrapping for *.zip package files, like when used with url http://.../document.php?modulepart=packages&file=module_myfile.zip
3188 // Dir for custom dirs
3189 $tmp = explode(',', $dolibarr_main_document_root_alt);
3190 $dirins = $tmp[0];
3191
3192 $accessallowed = ($user->admin && preg_match('/^module_.*\.zip$/', basename($original_file)));
3193 $original_file = $dirins.'/'.$original_file;
3194 } elseif ($modulepart == 'mycompany' && !empty($conf->mycompany->dir_output)) {
3195 // Wrapping for some images
3196 $accessallowed = 1;
3197 $original_file = $conf->mycompany->dir_output.'/'.$original_file;
3198 } elseif ($modulepart == 'userphoto' && !empty($conf->user->dir_output)) {
3199 // Wrapping for users photos (user photos are allowed to any connected users)
3200 $accessallowed = 0;
3201 if (preg_match('/^\d+\/photos\//', $original_file)) {
3202 $accessallowed = 1;
3203 }
3204 $original_file = $conf->user->dir_output.'/'.$original_file;
3205 } elseif ($modulepart == 'userphotopublic' && !empty($conf->user->dir_output)) {
3206 // Wrapping for users photos that were set to public (for virtual credit card) by their owner (public user photos can be read
3207 // with the public link and securekey)
3208 $accessok = false;
3209 $reg = array();
3210 if (preg_match('/^(\d+)\/photos\//', $original_file, $reg)) {
3211 if ((int) $reg[1]) {
3212 $tmpobject = new User($db);
3213 $tmpobject->fetch((int) $reg[1], '', '', 1);
3214 if (getDolUserInt('USER_ENABLE_PUBLIC', 0, $tmpobject)) {
3215 $securekey = GETPOST('securekey', 'alpha', 1);
3216 // Security check
3217 global $dolibarr_main_cookie_cryptkey, $dolibarr_main_instance_unique_id;
3218 $valuetouse = $dolibarr_main_instance_unique_id ? $dolibarr_main_instance_unique_id : $dolibarr_main_cookie_cryptkey; // Use $dolibarr_main_instance_unique_id first then $dolibarr_main_cookie_cryptkey
3219 $encodedsecurekey = dol_hash($valuetouse.'uservirtualcard'.$tmpobject->id.'-'.$tmpobject->login, 'md5');
3220 if ($encodedsecurekey == $securekey) {
3221 $accessok = true;
3222 }
3223 }
3224 }
3225 }
3226 if ($accessok) {
3227 $accessallowed = 1;
3228 }
3229 $original_file = $conf->user->dir_output.'/'.$original_file;
3230 } elseif (($modulepart == 'companylogo') && !empty($conf->mycompany->dir_output)) {
3231 // Wrapping for company logos (company logos are allowed to anyboby, they are public)
3232 $accessallowed = 1;
3233 $original_file = $conf->mycompany->dir_output.'/logos/'.$original_file;
3234 } elseif ($modulepart == 'memberphoto' && !empty($conf->member->dir_output)) {
3235 // Wrapping for members photos
3236 $accessallowed = 0;
3237 // Simple chosen for automatic generation of member codes
3238 if (preg_match('/^\d+\/photos\//', $original_file)) {
3239 $accessallowed = 1;
3240 }
3241 // Advanced chosen for automatic generation of member codes
3242 if (preg_match('/^MEM\d\d\d\d-\d\d\d\d\/photos\//', $original_file)) {
3243 $accessallowed = 1;
3244 }
3245 $original_file = $conf->member->dir_output.'/'.$original_file;
3246 } elseif ($modulepart == 'apercufacture' && !empty($conf->invoice->multidir_output[$entity])) {
3247 // Wrapping for invoices (user need permission to read invoices)
3248 if ($fuser->hasRight('facture', $lire)) {
3249 $accessallowed = 1;
3250 }
3251 $original_file = $conf->invoice->multidir_output[$entity].'/'.$original_file;
3252 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('invoice').")";
3253 } elseif ($modulepart == 'apercupropal' && !empty($conf->propal->multidir_output[$entity])) {
3254 // Wrapping for preview of proposals
3255 if ($fuser->hasRight('propal', $lire)) {
3256 $accessallowed = 1;
3257 }
3258 $original_file = $conf->propal->multidir_output[$entity].'/'.$original_file;
3259 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."propal WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('propal').")";
3260 } elseif ($modulepart == 'apercucommande' && !empty($conf->order->multidir_output[$entity])) {
3261 // Wrapping for preview of orders
3262 if ($fuser->hasRight('commande', $lire)) {
3263 $accessallowed = 1;
3264 }
3265 $original_file = $conf->order->multidir_output[$entity].'/'.$original_file;
3266 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('order').")";
3267 } elseif (($modulepart == 'apercufichinter' || $modulepart == 'apercuficheinter') && !empty($conf->ficheinter->multidir_output[$entity])) {
3268 // Wrapping for preview of intervention
3269 if ($fuser->hasRight('ficheinter', $lire)) {
3270 $accessallowed = 1;
3271 }
3272 $original_file = $conf->ficheinter->multidir_output[$entity].'/'.$original_file;
3273 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3274 } elseif (($modulepart == 'apercucontract') && !empty($conf->contract->multidir_output[$entity])) {
3275 // Wrapping for preview of contracts
3276 if ($fuser->hasRight('contrat', $lire)) {
3277 $accessallowed = 1;
3278 }
3279 $original_file = $conf->contract->multidir_output[$entity].'/'.$original_file;
3280 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."contrat WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('contract').")";
3281 } elseif (($modulepart == 'apercusupplier_proposal') && !empty($conf->supplier_proposal->dir_output)) {
3282 // Wrapping for preview of vendor proposals
3283 if ($fuser->hasRight('supplier_proposal', $lire)) {
3284 $accessallowed = 1;
3285 }
3286 $original_file = $conf->supplier_proposal->dir_output.'/'.$original_file;
3287 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."supplier_proposal WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('supplier_proposal').")";
3288 } elseif (($modulepart == 'apercusupplier_order') && !empty($conf->fournisseur->commande->dir_output)) {
3289 // Wrapping for preview of purchase orders
3290 if ($fuser->hasRight('fournisseur', 'commande', $lire)) {
3291 $accessallowed = 1;
3292 }
3293 $original_file = $conf->fournisseur->commande->dir_output.'/'.$original_file;
3294 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3295 } elseif (($modulepart == 'apercusupplier_invoice') && !empty($conf->fournisseur->facture->dir_output)) {
3296 // Wrapping for preview of supplier invoices
3297 if ($fuser->hasRight('fournisseur', $lire)) {
3298 $accessallowed = 1;
3299 }
3300 $original_file = $conf->fournisseur->facture->dir_output.'/'.$original_file;
3301 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture_fourn WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3302 } elseif (($modulepart == 'holiday') && !empty($conf->holiday->dir_output)) {
3303 if ($fuser->hasRight('holiday', $read) || $fuser->hasRight('holiday', 'readall') || preg_match('/^specimen/i', $original_file)) {
3304 $accessallowed = 1;
3305 // If we known $id of holiday, call checkUserAccessToObject to check permission on properties and hierarchy of leave request
3306 if ($refname && !$fuser->hasRight('holiday', 'readall') && !preg_match('/^specimen/i', $original_file)) {
3307 include_once DOL_DOCUMENT_ROOT.'/holiday/class/holiday.class.php';
3308 $tmpholiday = new Holiday($db);
3309 $tmpholiday->fetch(0, $refname);
3310 $accessallowed = checkUserAccessToObject($user, array('holiday'), $tmpholiday, 'holiday', '', '', 'rowid', '');
3311 }
3312 }
3313 $original_file = $conf->holiday->dir_output.'/'.$original_file;
3314 } elseif (($modulepart == 'salaries') && !empty($conf->salaries->dir_output)) {
3315 // Wrapping for salaries. The subdirectory is the id of the salary, see salaries/document.php.
3316 if ($fuser->hasRight('salaries', $read) || $fuser->hasRight('salaries', 'readall') || preg_match('/^specimen/i', $original_file)) {
3317 $accessallowed = 1;
3318 // The 'read' permission is labelled "yours only" and the two screens leading to this
3319 // download, salaries/card.php and salaries/document.php, do enforce it on fk_user.
3320 // checkUserAccessToObject() only tests the entity for this feature, so the same rule has to
3321 // be applied here: without it any holder of salaries->read downloads every payslip.
3322 if ($refname && !$fuser->hasRight('salaries', 'readall') && !preg_match('/^specimen/i', $original_file)) {
3323 include_once DOL_DOCUMENT_ROOT.'/salaries/class/salary.class.php';
3324 $tmpsalary = new Salary($db);
3325 $tmpsalary->fetch((int) $refname);
3326 // Same condition as salaries/card.php and salaries/document.php, word for word.
3327 // getAllChildIds(1) includes the current user, so this covers their own payslip as well
3328 // as those of the users below them. The test on fk_user also closes the case of an id
3329 // that matches no salary, Salary::fetch() returning 1 even then.
3330 $accessallowed = ($tmpsalary->fk_user > 0 && in_array($tmpsalary->fk_user, $fuser->getAllChildIds(1))) ? 1 : 0;
3331 }
3332 }
3333 $original_file = $conf->salaries->dir_output.'/'.$original_file;
3334 } elseif (($modulepart == 'expensereport') && !empty($conf->expensereport->dir_output)) {
3335 if ($fuser->hasRight('expensereport', $lire) || $fuser->hasRight('expensereport', 'readall') || preg_match('/^specimen/i', $original_file)) {
3336 $accessallowed = 1;
3337 // If we known $id of expensereport, call checkUserAccessToObject to check permission on properties and hierarchy of expense report
3338 if ($refname && !$fuser->hasRight('expensereport', 'readall') && !preg_match('/^specimen/i', $original_file)) {
3339 include_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
3340 $tmpexpensereport = new ExpenseReport($db);
3341 $tmpexpensereport->fetch(0, $refname);
3342 $accessallowed = checkUserAccessToObject($user, array('expensereport'), $tmpexpensereport, 'expensereport', '', '', 'rowid', '');
3343 }
3344 }
3345 $original_file = $conf->expensereport->dir_output.'/'.$original_file;
3346 } elseif (($modulepart == 'apercuexpensereport') && !empty($conf->expensereport->dir_output)) {
3347 // Wrapping for preview of expense report
3348 if ($fuser->hasRight('expensereport', $lire)) {
3349 $accessallowed = 1;
3350 // An expense report is always owned by an internal user, so an external user can never be allowed to access it
3351 if ($fuser->socid > 0) {
3352 $accessallowed = 0;
3353 }
3354 }
3355 $original_file = $conf->expensereport->dir_output.'/'.$original_file;
3356 } elseif ($modulepart == 'propalstats' && !empty($conf->propal->multidir_temp[$entity])) {
3357 // Wrapping for statistics images of proposal
3358 if ($fuser->hasRight('propal', $lire)) {
3359 $accessallowed = 1;
3360 }
3361 $original_file = $conf->propal->multidir_temp[$entity].'/'.$original_file;
3362 } elseif ($modulepart == 'orderstats' && !empty($conf->order->dir_temp)) {
3363 // Wrapping for statistics images of orders
3364 if ($fuser->hasRight('commande', $lire)) {
3365 $accessallowed = 1;
3366 }
3367 $original_file = $conf->order->dir_temp.'/'.$original_file;
3368 } elseif ($modulepart == 'orderstatssupplier' && !empty($conf->fournisseur->dir_output)) {
3369 if ($fuser->hasRight('fournisseur', 'commande', $lire)) {
3370 $accessallowed = 1;
3371 }
3372 $original_file = $conf->fournisseur->commande->dir_temp.'/'.$original_file;
3373 } elseif ($modulepart == 'billstats' && !empty($conf->invoice->dir_temp)) {
3374 // Wrapping for statistics images of purchase orders
3375 if ($fuser->hasRight('facture', $lire)) {
3376 $accessallowed = 1;
3377 }
3378 $original_file = $conf->invoice->dir_temp.'/'.$original_file;
3379 } elseif ($modulepart == 'billstatssupplier' && !empty($conf->fournisseur->dir_output)) {
3380 if ($fuser->hasRight('fournisseur', 'facture', $lire)) {
3381 $accessallowed = 1;
3382 }
3383 $original_file = $conf->fournisseur->facture->dir_temp.'/'.$original_file;
3384 } elseif ($modulepart == 'expeditionstats' && !empty($conf->expedition->dir_temp)) {
3385 // Wrapping for statistics images of shipments
3386 if ($fuser->hasRight('expedition', $lire)) {
3387 $accessallowed = 1;
3388 }
3389 $original_file = $conf->expedition->dir_temp.'/'.$original_file;
3390 } elseif ($modulepart == 'memberstats' && !empty($conf->member->dir_temp)) {
3391 // Wrapping for statistics images of memberships
3392 if ($fuser->hasRight('adherent', $lire)) {
3393 $accessallowed = 1;
3394 }
3395 $original_file = $conf->member->dir_temp.'/'.$original_file;
3396 } elseif (preg_match('/^productstats_/i', $modulepart) && !empty($conf->product->dir_temp)) {
3397 // Wrapping for statistics images of products
3398 if ($fuser->hasRight('produit', $lire) || $fuser->hasRight('service', $lire)) {
3399 $accessallowed = 1;
3400 }
3401 $original_file = (!empty($conf->product->multidir_temp[$entity]) ? $conf->product->multidir_temp[$entity] : $conf->service->multidir_temp[$entity]).'/'.$original_file;
3402 } elseif (in_array($modulepart, array('tax', 'tax-vat', 'tva')) && !empty($conf->tax->dir_output)) {
3403 // Wrapping for taxes
3404 if ($fuser->hasRight('tax', 'charges', $lire)) {
3405 $accessallowed = 1;
3406 }
3407 $modulepartsuffix = str_replace('tax-', '', $modulepart);
3408 $original_file = $conf->tax->dir_output.'/'.($modulepartsuffix != 'tax' ? $modulepartsuffix.'/' : '').$original_file;
3409 } elseif (($modulepart == 'actions' || $modulepart == 'actioncomm') && !empty($conf->agenda->dir_output)) {
3410 // Wrapping for events
3411 if ($fuser->hasRight('agenda', 'myactions', $read)) {
3412 $accessallowed = 1;
3413 // If we known $id of project, call checkUserAccessToObject to check permission on the given agenda event on properties and assigned users
3414 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3415 include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3416 $tmpobject = new ActionComm($db);
3417 $tmpobject->fetch((int) $refname);
3418 $accessallowed = checkUserAccessToObject($user, array('agenda'), $tmpobject->id, 'actioncomm&societe', 'myactions|allactions', 'fk_soc', 'id', '');
3419 if ($user->socid && $tmpobject->socid) {
3420 $accessallowed = checkUserAccessToObject($user, array('societe'), $tmpobject->socid);
3421 }
3422 }
3423 }
3424 $original_file = $conf->agenda->dir_output.'/'.$original_file;
3425 } elseif ($modulepart == 'category' && !empty($conf->categorie->multidir_output[$entity])) {
3426 // Wrapping for categories (categories are allowed if user has permission to read categories or to work on TakePos)
3427 if (empty($entity) || empty($conf->categorie->multidir_output[$entity])) {
3428 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3429 }
3430 if ($fuser->hasRight("categorie", $lire) || $fuser->hasRight("takepos", "run")) {
3431 $accessallowed = 1;
3432 }
3433 $original_file = $conf->categorie->multidir_output[$entity].'/'.$original_file;
3434 } elseif ($modulepart == 'prelevement' && !empty($conf->prelevement->dir_output)) {
3435 // Wrapping for direct debits
3436 if ($fuser->hasRight('prelevement', 'bons', $lire) || preg_match('/^specimen/i', $original_file)) {
3437 $accessallowed = 1;
3438 }
3439 $original_file = $conf->prelevement->dir_output.'/'.$original_file;
3440 } elseif ($modulepart == 'graph_stock' && !empty($conf->stock->dir_temp)) {
3441 // Wrapping for energy graphs
3442 $accessallowed = 1;
3443 $original_file = $conf->stock->dir_temp.'/'.$original_file;
3444 } elseif ($modulepart == 'graph_fourn' && !empty($conf->fournisseur->dir_temp)) {
3445 // Wrapping for supplier graphs
3446 $accessallowed = 1;
3447 $original_file = $conf->fournisseur->dir_temp.'/'.$original_file;
3448 } elseif ($modulepart == 'graph_product' && !empty($conf->product->dir_temp)) {
3449 // Wrapping for product graphs
3450 $accessallowed = 1;
3451 $original_file = $conf->product->multidir_temp[$entity].'/'.$original_file;
3452 } elseif ($modulepart == 'barcode') {
3453 // Wrapping for barcodes
3454 $accessallowed = 1;
3455 // If viewimage is called for barcode, we try to output an image on the fly, with no build of file on disk.
3456 //$original_file=$conf->barcode->dir_temp.'/'.$original_file;
3457 $original_file = '';
3458 } elseif ($modulepart == 'iconmailing' && !empty($conf->mailing->dir_temp)) {
3459 // Wrapping for icon of background of mailings
3460 $accessallowed = 1;
3461 $original_file = $conf->mailing->dir_temp.'/'.$original_file;
3462 } elseif ($modulepart == 'scanner_user_temp' && !empty($conf->scanner->dir_temp)) {
3463 // Wrapping for the scanner
3464 $accessallowed = 1;
3465 $original_file = $conf->scanner->dir_temp.'/'.$fuser->id.'/'.$original_file;
3466 } elseif ($modulepart == 'fckeditor' && !empty($conf->fckeditor->dir_output)) {
3467 // Wrapping for fckeditor images
3468 $accessallowed = 1;
3469 $original_file = $conf->fckeditor->dir_output.'/'.$original_file;
3470 } elseif ($modulepart == 'user' && !empty($conf->user->dir_output)) {
3471 // Wrapping for users
3472 $canreaduser = (!empty($fuser->admin) || $fuser->hasRight('user', 'user', $lire));
3473 if ($fuser->id == (int) $refname) {
3474 $canreaduser = 1;
3475 } // A user can always read its own card
3476 if ($canreaduser || preg_match('/^specimen/i', $original_file)) {
3477 $accessallowed = 1;
3478 }
3479 $original_file = $conf->user->dir_output.'/'.$original_file;
3480 } elseif (($modulepart == 'company' || $modulepart == 'societe' || $modulepart == 'thirdparty') && !empty($conf->societe->multidir_output[$entity])) {
3481 // Wrapping for third parties
3482 if (empty($entity) || empty($conf->societe->multidir_output[$entity])) {
3483 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3484 }
3485 if ($fuser->hasRight('societe', $lire) || preg_match('/^specimen/i', $original_file)) {
3486 $accessallowed = 1;
3487 }
3488 $original_file = $conf->societe->multidir_output[$entity].'/'.$original_file;
3489 $sqlprotectagainstexternals = "SELECT rowid as fk_soc FROM ".MAIN_DB_PREFIX."societe WHERE rowid = ".((int) $refname)." AND entity IN (".getEntity('societe').")";
3490 } elseif (($modulepart == 'contact' || $modulepart == 'socpeople') && !empty($conf->societe->multidir_output[$entity])) {
3491 // Wrapping for contact
3492 if (empty($entity) || empty($conf->societe->multidir_output[$entity])) {
3493 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3494 }
3495 if ($fuser->hasRight('societe', 'contact', $lire)) {
3496 $accessallowed = 1;
3497 }
3498 $original_file = $conf->societe->multidir_output[$entity].'/contact/'.$original_file;
3499 $sqlprotectagainstexternals = "SELECT fk_soc FROM ".MAIN_DB_PREFIX."socpeople WHERE rowid = ".((int) $refname)." AND entity IN (".getEntity('contact').")";
3500 } elseif (($modulepart == 'facture' || $modulepart == 'invoice') && !empty($conf->invoice->multidir_output[$entity])) {
3501 // Wrapping for invoices
3502 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3503 $accessallowed = 1;
3504 }
3505 $original_file = $conf->invoice->multidir_output[$entity].'/'.$original_file;
3506 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('invoice').")";
3507 } elseif ($modulepart == 'massfilesarea_proposals' && !empty($conf->propal->multidir_output[$entity])) {
3508 // Wrapping for mass actions
3509 if ($fuser->hasRight('propal', $lire) || preg_match('/^specimen/i', $original_file)) {
3510 $accessallowed = 1;
3511 }
3512 $original_file = $conf->propal->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3513 } elseif ($modulepart == 'massfilesarea_orders') {
3514 if ($fuser->hasRight('commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3515 $accessallowed = 1;
3516 }
3517 $original_file = $conf->order->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3518 } elseif ($modulepart == 'massfilesarea_sendings') {
3519 if ($fuser->hasRight('expedition', $lire) || preg_match('/^specimen/i', $original_file)) {
3520 $accessallowed = 1;
3521 }
3522 $original_file = $conf->expedition->dir_output.'/sending/temp/massgeneration/'.$user->id.'/'.$original_file;
3523 } elseif ($modulepart == 'massfilesarea_receipts') {
3524 if ($fuser->hasRight('reception', $lire) || preg_match('/^specimen/i', $original_file)) {
3525 $accessallowed = 1;
3526 }
3527 $original_file = $conf->reception->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3528 } elseif ($modulepart == 'massfilesarea_invoices') {
3529 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3530 $accessallowed = 1;
3531 }
3532 $original_file = $conf->invoice->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3533 } elseif ($modulepart == 'massfilesarea_expensereport') {
3534 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3535 $accessallowed = 1;
3536 }
3537 $original_file = $conf->expensereport->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3538 } elseif ($modulepart == 'massfilesarea_interventions') {
3539 if ($fuser->hasRight('ficheinter', $lire) || preg_match('/^specimen/i', $original_file)) {
3540 $accessallowed = 1;
3541 }
3542 $original_file = $conf->ficheinter->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3543 } elseif ($modulepart == 'massfilesarea_supplier_proposal' && !empty($conf->supplier_proposal->dir_output)) {
3544 if ($fuser->hasRight('supplier_proposal', $lire) || preg_match('/^specimen/i', $original_file)) {
3545 $accessallowed = 1;
3546 }
3547 $original_file = $conf->supplier_proposal->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3548 } elseif ($modulepart == 'massfilesarea_supplier_order') {
3549 if ($fuser->hasRight('fournisseur', 'commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3550 $accessallowed = 1;
3551 }
3552 $original_file = $conf->fournisseur->commande->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3553 } elseif ($modulepart == 'massfilesarea_supplier_invoice') {
3554 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3555 $accessallowed = 1;
3556 }
3557 $original_file = $conf->fournisseur->facture->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3558 } elseif ($modulepart == 'massfilesarea_contract' && !empty($conf->contract->dir_output)) {
3559 if ($fuser->hasRight('contrat', $lire) || preg_match('/^specimen/i', $original_file)) {
3560 $accessallowed = 1;
3561 }
3562 $original_file = $conf->contract->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3563 } elseif ($modulepart == 'massfilesarea_stock' && !empty($conf->stock->dir_output)) {
3564 if ($fuser->hasRight('stock', $lire) || preg_match('/^specimen/i', $original_file)) {
3565 $accessallowed = 1;
3566 }
3567 $original_file = $conf->stock->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3568 } elseif (($modulepart == 'fichinter' || $modulepart == 'ficheinter') && !empty($conf->ficheinter->multidir_output[$entity])) {
3569 // Wrapping for interventions
3570 if ($fuser->hasRight('ficheinter', $lire) || preg_match('/^specimen/i', $original_file)) {
3571 $accessallowed = 1;
3572 }
3573 $original_file = $conf->ficheinter->multidir_output[$entity].'/'.$original_file;
3574 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3575 } elseif (($modulepart == 'propal' || $modulepart == 'propale') && isset($conf->propal->multidir_output[$entity])) {
3576 // Wrapping for proposals
3577 if ($fuser->hasRight('propal', $lire) || preg_match('/^specimen/i', $original_file)) {
3578 $accessallowed = 1;
3579 }
3580 $original_file = $conf->propal->multidir_output[$entity].'/'.$original_file;
3581 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."propal WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('propal').")";
3582 } elseif (($modulepart == 'commande' || $modulepart == 'order') && !empty($conf->order->multidir_output[$entity])) {
3583 // Wrapping for orders
3584 if ($fuser->hasRight('commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3585 $accessallowed = 1;
3586 }
3587 $original_file = $conf->order->multidir_output[$entity].'/'.$original_file;
3588 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('order').")";
3589 } elseif ($modulepart == 'project' && !empty($conf->project->multidir_output[$entity])) {
3590 // Wrapping for projects
3591 if ($fuser->hasRight('projet', $lire) || preg_match('/^specimen/i', $original_file)) {
3592 $accessallowed = 1;
3593 // If we known $id of project, call checkUserAccessToObject to check permission on properties and contact of project
3594 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3595 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
3596 $tmpproject = new Project($db);
3597 $tmpproject->fetch(0, $refname);
3598 $accessallowed = checkUserAccessToObject($user, array('projet'), $tmpproject->id, 'projet&project', '', '', 'rowid', '');
3599 }
3600 }
3601 $original_file = $conf->project->multidir_output[$entity].'/'.$original_file;
3602 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
3603 } elseif ($modulepart == 'project_task' && !empty($conf->project->multidir_output[$entity])) {
3604 if ($fuser->hasRight('projet', $lire) || preg_match('/^specimen/i', $original_file)) {
3605 $accessallowed = 1;
3606 // If we known $id of project, call checkUserAccessToObject to check permission on properties and contact of project
3607 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3608 include_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
3609 $tmptask = new Task($db);
3610 $tmptask->fetch(0, $refname);
3611 $accessallowed = checkUserAccessToObject($user, array('projet_task'), $tmptask->id, 'projet_task&project', '', '', 'rowid', '');
3612 }
3613 }
3614 $original_file = $conf->project->multidir_output[$entity].'/'.$original_file;
3615 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
3616 } elseif (($modulepart == 'commande_fournisseur' || $modulepart == 'order_supplier' || $modulepart == 'supplier_order') && !empty($conf->fournisseur->commande->dir_output)) {
3617 // Wrapping for purchase orders
3618 if ($fuser->hasRight('fournisseur', 'commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3619 $accessallowed = 1;
3620 }
3621 $original_file = $conf->fournisseur->commande->dir_output.'/'.$original_file;
3622 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE ref='".$db->escape($refname)."' AND entity = ".((int) $conf->entity);
3623 } elseif (($modulepart == 'facture_fournisseur' || $modulepart == 'invoice_supplier' || $modulepart == 'supplier_invoice') && !empty($conf->fournisseur->facture->dir_output)) {
3624 // Wrapping for supplier invoices
3625 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3626 $accessallowed = 1;
3627 }
3628 $original_file = $conf->fournisseur->facture->dir_output.'/'.$original_file;
3629 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture_fourn WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3630 } elseif ($modulepart == 'supplier_payment') {
3631 // Wrapping for supplier payments
3632 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3633 $accessallowed = 1;
3634 }
3635 $original_file = preg_replace("/payment\//", "", $original_file); // Because the $conf->fournisseur->payment->dir_output already contains the "payment/"
3636 $original_file = $conf->fournisseur->payment->dir_output.'/'.$original_file;
3637 $sqlprotectagainstexternals = "SELECT f.fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."paiementfourn as p";
3638 $sqlprotectagainstexternals .= " INNER JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON pf.fk_paiementfourn = p.rowid";
3639 $sqlprotectagainstexternals .= " INNER JOIN ".MAIN_DB_PREFIX."facture_fourn as f ON pf.fk_facturefourn = p.rowid";
3640 $sqlprotectagainstexternals .= " WHERE p.ref = '".$db->escape($refname)."' AND p.entity=".((int) $conf->entity);
3641 } elseif ($modulepart == 'payment') {
3642 // Wrapping for report of payments
3643 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3644 $accessallowed = 1;
3645 }
3646 $original_file = $conf->compta->payment->dir_output.'/'.$original_file;
3647 } elseif ($modulepart == 'facture_paiement' && !empty($conf->invoice->dir_output)) {
3648 // Wrapping for report of payments
3649 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3650 $accessallowed = 1;
3651 }
3652 if ($fuser->socid > 0) {
3653 $original_file = $conf->invoice->dir_output.'/payments/private/'.$fuser->id.'/'.$original_file;
3654 } else {
3655 $original_file = $conf->invoice->dir_output.'/payments/'.$original_file;
3656 }
3657 /* $sqlprotectagainstexternals = "SELECT f.fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."paiement as p";
3658 $sqlprotectagainstexternals .= " INNER JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON pf.fk_paiement = p.rowid";
3659 $sqlprotectagainstexternals .= " INNER JOIN ".MAIN_DB_PREFIX."facture as f ON pf.fk_facture = p.rowid";
3660 $sqlprotectagainstexternals .= " WHERE p.ref = '".$db->escape($refname)."' AND p.entity=".((int) $conf->entity);
3661 var_dump($sqlprotectagainstexternals);exit;*/
3662 } elseif ($modulepart == 'accounting' && !empty($conf->accounting->dir_output)) {
3663 // Wrapping for accounting exports
3664 if ($fuser->hasRight('accounting', 'bind', 'write') || $fuser->hasRight('accounting', 'mouvements', 'export') || preg_match('/^specimen/i', $original_file)) {
3665 $accessallowed = 1;
3666 }
3667 $original_file = $conf->accounting->dir_output.'/'.$original_file;
3668 } elseif (($modulepart == 'expedition' || $modulepart == 'shipment' || $modulepart == 'shipping') && !empty($conf->expedition->dir_output)) {
3669 // Wrapping for shipments
3670 if ($fuser->hasRight('expedition', $lire) || preg_match('/^specimen/i', $original_file)) {
3671 $accessallowed = 1;
3672 }
3673 $original_file = $conf->expedition->dir_output."/".(strpos($original_file, 'sending/') === 0 ? '' : 'sending/').$original_file;
3674 //$original_file = $conf->expedition->dir_output."/".$original_file;
3675 } elseif (($modulepart == 'livraison' || $modulepart == 'delivery') && !empty($conf->expedition->dir_output)) {
3676 // Delivery Note Wrapping
3677 if ($fuser->hasRight('expedition', 'delivery', $lire) || preg_match('/^specimen/i', $original_file)) {
3678 $accessallowed = 1;
3679 }
3680 $original_file = $conf->expedition->dir_output."/".(strpos($original_file, 'receipt/') === 0 ? '' : 'receipt/').$original_file;
3681 } elseif ($modulepart == 'actionsreport' && !empty($conf->agenda->dir_temp)) {
3682 // Wrapping for actions
3683 if ($fuser->hasRight('agenda', 'allactions', $read) || preg_match('/^specimen/i', $original_file)) {
3684 $accessallowed = 1;
3685 }
3686 $original_file = $conf->agenda->dir_temp."/".$original_file;
3687 } elseif ($modulepart == 'product' || $modulepart == 'produit' || $modulepart == 'service' || $modulepart == 'produit|service') {
3688 // Wrapping for products and services
3689 if (empty($entity) || (empty($conf->product->multidir_output[$entity]) && empty($conf->service->multidir_output[$entity]))) {
3690 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3691 }
3692 if (($fuser->hasRight('produit', $lire) || $fuser->hasRight('service', $lire)) || preg_match('/^specimen/i', $original_file)) {
3693 $accessallowed = 1;
3694 }
3695 if (isModEnabled("product")) {
3696 $original_file = $conf->product->multidir_output[$entity].'/'.$original_file;
3697 } elseif (isModEnabled("service")) {
3698 $original_file = $conf->service->multidir_output[$entity].'/'.$original_file;
3699 }
3700 } elseif ($modulepart == 'product_batch' || $modulepart == 'produitlot') {
3701 // Wrapping for product lots
3702 if (empty($entity) || (empty($conf->productbatch->multidir_output[$entity]))) {
3703 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3704 }
3705 if (($fuser->hasRight('produit', $lire)) || preg_match('/^specimen/i', $original_file)) {
3706 $accessallowed = 1;
3707 }
3708 if (isModEnabled('productbatch')) {
3709 $original_file = $conf->productbatch->multidir_output[$entity].'/'.$original_file;
3710 }
3711 } elseif ($modulepart == 'movement' || $modulepart == 'mouvement') {
3712 // Wrapping for stock movements
3713 if (empty($entity) || empty($conf->stock->multidir_output[$entity])) {
3714 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3715 }
3716 if (($fuser->hasRight('stock', $lire) || $fuser->hasRight('stock', 'movement', $lire) || $fuser->hasRight('stock', 'mouvement', $lire)) || preg_match('/^specimen/i', $original_file)) {
3717 $accessallowed = 1;
3718 }
3719 if (isModEnabled('stock')) {
3720 $original_file = $conf->stock->multidir_output[$entity].'/movement/'.$original_file;
3721 }
3722 } elseif ($modulepart == 'inventory') {
3723 // Wrapping for stock inventories
3724 if (empty($entity) || empty($conf->stock->multidir_output[$entity])) {
3725 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3726 }
3727 if ($fuser->hasRight('stock', $lire) || preg_match('/^specimen/i', $original_file)) {
3728 $accessallowed = 1;
3729 }
3730 if (isModEnabled('stock')) {
3731 $original_file = $conf->stock->multidir_output[$entity].'/inventory/'.$original_file;
3732 }
3733 } elseif ($modulepart == 'entrepot') {
3734 // Wrapping for stock warehouse
3735 if (empty($entity) || empty($conf->stock->multidir_output[$entity])) {
3736 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3737 }
3738 if (($fuser->hasRight('stock', $lire) || $fuser->hasRight('stock', 'movement', $lire) || $fuser->hasRight('stock', 'mouvement', $lire)) || preg_match('/^specimen/i', $original_file)) {
3739 $accessallowed = 1;
3740 }
3741 if (isModEnabled('stock')) {
3742 $original_file = $conf->stock->multidir_output[$entity].'/'.$original_file;
3743 }
3744 } elseif ($modulepart == 'contract' && !empty($conf->contract->multidir_output[$entity])) {
3745 // Wrapping for contracts
3746 if ($fuser->hasRight('contrat', $lire) || preg_match('/^specimen/i', $original_file)) {
3747 $accessallowed = 1;
3748 }
3749 $original_file = $conf->contract->multidir_output[$entity].'/'.$original_file;
3750 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."contrat WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('contract').")";
3751 } elseif ($modulepart == 'donation' && !empty($conf->don->dir_output)) {
3752 // Wrapping for donation
3753 if ($fuser->hasRight('don', $lire) || preg_match('/^specimen/i', $original_file)) {
3754 $accessallowed = 1;
3755 }
3756 $original_file = $conf->don->dir_output.'/'.$original_file;
3757 } elseif ($modulepart == 'dolresource' && !empty($conf->resource->dir_output)) {
3758 // Wrapping for resources
3759 if ($fuser->hasRight('resource', $read) || preg_match('/^specimen/i', $original_file)) {
3760 $accessallowed = 1;
3761 }
3762 $original_file = $conf->resource->dir_output.'/'.$original_file;
3763 } elseif (($modulepart == 'remisecheque' || $modulepart == 'chequereceipt') && !empty($conf->bank->dir_output)) {
3764 // Wrapping for check deposits
3765 if ($fuser->hasRight('banque', $lire) || preg_match('/^specimen/i', $original_file)) {
3766 $accessallowed = 1;
3767 }
3768 $original_file = $conf->bank->dir_output.'/checkdeposits/'.$original_file; // original_file should contains relative path so include the get_exdir result
3769 } elseif (($modulepart == 'banque' || $modulepart == 'bank') && !empty($conf->bank->dir_output)) {
3770 // Wrapping for bank
3771 // The bank module has no 'creer' permission: writing a bank file requires 'modifier', like account_statement_document.php
3772 if ($fuser->hasRight('banque', ($mode == 'read' ? 'lire' : 'modifier'))) {
3773 $accessallowed = 1;
3774 }
3775 $original_file = $conf->bank->dir_output.'/'.$original_file;
3776 } elseif ($modulepart == 'export' && !empty($conf->export->dir_temp)) {
3777 // Wrapping for export module
3778 // Note that a test may not be required because we force the dir of download on the directory of the user that export
3779 $accessallowed = $user->hasRight('export', 'lire');
3780 $original_file = $conf->export->dir_temp.'/'.$fuser->id.'/'.$original_file;
3781 } elseif ($modulepart == 'import' && !empty($conf->import->dir_temp)) {
3782 // Wrapping for import module
3783 $accessallowed = $user->hasRight('import', 'run');
3784 $original_file = $conf->import->dir_temp.'/'.$original_file;
3785 } elseif ($modulepart == 'recruitment' && !empty($conf->recruitment->dir_output)) {
3786 // Wrapping for recruitment module
3787 $accessallowed = $user->hasRight('recruitment', 'recruitmentjobposition', 'read');
3788 $original_file = $conf->recruitment->dir_output.'/'.$original_file;
3789 } elseif ($modulepart == 'hrm' && !empty($conf->hrm->dir_output)) {
3790 // Wrapping for hrm module
3791 $accessallowed = $user->hasRight('hrm', 'all', 'read');
3792 $original_file = $conf->hrm->dir_output.'/'.$original_file;
3793 } elseif ($modulepart == 'editor' && !empty($conf->fckeditor->dir_output)) {
3794 // Wrapping for wysiwyg editor
3795 $accessallowed = 1;
3796 $original_file = $conf->fckeditor->dir_output.'/'.$original_file;
3797 } elseif ($modulepart == 'systemtools' && !empty($conf->admin->dir_output)) {
3798 // Wrapping for backups
3799 if ($fuser->admin) {
3800 $accessallowed = 1;
3801 }
3802 $original_file = $conf->admin->dir_output.'/'.$original_file;
3803 } elseif ($modulepart == 'admin_temp' && !empty($conf->admin->dir_temp)) {
3804 // Wrapping for upload file test
3805 if ($fuser->admin) {
3806 $accessallowed = 1;
3807 }
3808 $original_file = $conf->admin->dir_temp.'/'.$original_file;
3809 } elseif ($modulepart == 'bittorrent' && !empty($conf->bittorrent->dir_output)) {
3810 // Wrapping for BitTorrent
3811 $accessallowed = 1;
3812 $dir = 'files';
3813 if (dol_mimetype($original_file) == 'application/x-bittorrent') {
3814 $dir = 'torrents';
3815 }
3816 $original_file = $conf->bittorrent->dir_output.'/'.$dir.'/'.$original_file;
3817 } elseif ($modulepart == 'member' && !empty($conf->member->dir_output)) {
3818 // Wrapping for Foundation module
3819 if ($fuser->hasRight('adherent', $lire) || preg_match('/^specimen/i', $original_file)) {
3820 $accessallowed = 1;
3821 }
3822 $original_file = $conf->member->dir_output.'/'.$original_file;
3823 } elseif ($modulepart == 'ticket' && !empty($conf->ticket->multidir_output[$entity])) {
3824 // Wrapping for events
3825 if ($fuser->hasRight('ticket', $read)) {
3826 $accessallowed = 1;
3827 }
3828 if (!isset($_SESSION['email_customer'])) {
3829 // Request to check socid for external users
3830 $sqlprotectagainstexternals = "SELECT fk_soc FROM ".MAIN_DB_PREFIX."ticket WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3831 } else {
3832 $email_split = explode('@', $_SESSION['email_customer']);
3833
3834 $sqlprotectagainstexternals = 'SELECT t.rowid, t.fk_soc FROM '.MAIN_DB_PREFIX.'ticket t';
3835 $sqlprotectagainstexternals .= ' LEFT JOIN '.MAIN_DB_PREFIX.'element_contact ec ON ec.element_id = t.rowid';
3836 $sqlprotectagainstexternals .= ' LEFT JOIN '.MAIN_DB_PREFIX.'socpeople c ON c.rowid = ec.fk_socpeople';
3837 $sqlprotectagainstexternals .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_contact tc ON tc.element = "ticket" AND tc.rowid = ec.fk_c_type_contact';
3838 $sqlprotectagainstexternals .= " WHERE t.ref LIKE '".$db->escape($refname)."'";
3839 $sqlprotectagainstexternals .= ' AND (';
3840 $sqlprotectagainstexternals .= ' (';
3841 $sqlprotectagainstexternals .= ' tc.rowid IS NOT NULL';
3842 $sqlprotectagainstexternals .= " AND c.email = '".$db->escape($email_split[0]).'@'.$db->sanitize($email_split[1])."'";
3843 $sqlprotectagainstexternals .= ' )';
3844 $sqlprotectagainstexternals .= " OR t.origin_email = '".$db->escape($email_split[0]).'@'.$db->sanitize($email_split[1])."'";
3845 $sqlprotectagainstexternals .= ' )';
3846 }
3847 $original_file = $conf->ticket->multidir_output[$entity].'/'.$original_file;
3848 // If modulepart=module_user_temp Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp/iduser
3849 // If modulepart=module_temp Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp
3850 // If modulepart=module_user Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/iduser
3851 // If modulepart=module Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart
3852 // If modulepart=module-abc Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart
3853 } else {
3854 // GENERIC Wrapping
3855 //var_dump($modulepart);
3856 //var_dump($original_file);
3857 if (preg_match('/^specimen/i', $original_file)) {
3858 $accessallowed = 1; // If link to a file called specimen. Test must be done before changing $original_file int full path.
3859 }
3860 if ($fuser->admin) {
3861 $accessallowed = 1; // If user is admin
3862 }
3863
3864 // For external modules, we can have (modulepart=mymodule and original_file=myobject/...) or (modulepart=mymodule-myobject and original_file=...)
3865 $tmpmodulepart = explode('-', $modulepart);
3866 if (!empty($tmpmodulepart[1])) {
3867 $modulepart = $tmpmodulepart[0];
3868 $original_file = $tmpmodulepart[1].'/'.$original_file;
3869 }
3870
3871 // Define $accessallowed
3872 $reg = array();
3873 if (preg_match('/^([a-z]+)_user_temp$/i', $modulepart, $reg)) {
3874 $tmpmodule = $reg[1];
3875 if (empty($conf->$tmpmodule->dir_temp)) { // modulepart not supported
3876 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3877 exit;
3878 }
3879 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3880 $accessallowed = 1;
3881 }
3882 $original_file = $conf->{$reg[1]}->dir_temp.'/'.$fuser->id.'/'.$original_file;
3883 } elseif (preg_match('/^([a-z]+)_temp$/i', $modulepart, $reg)) {
3884 $tmpmodule = $reg[1];
3885 if (empty($conf->$tmpmodule->dir_temp)) { // modulepart not supported
3886 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3887 exit;
3888 }
3889 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3890 $accessallowed = 1;
3891 }
3892 $original_file = $conf->$tmpmodule->dir_temp.'/'.$original_file;
3893 } elseif (preg_match('/^([a-z]+)_user$/i', $modulepart, $reg)) {
3894 $tmpmodule = $reg[1];
3895 if (empty($conf->$tmpmodule->dir_output)) { // modulepart not supported
3896 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3897 exit;
3898 }
3899 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3900 $accessallowed = 1;
3901 }
3902 $original_file = $conf->$tmpmodule->dir_output.'/'.$fuser->id.'/'.$original_file;
3903 } elseif (preg_match('/^massfilesarea_([a-z]+)$/i', $modulepart, $reg)) {
3904 $tmpmodule = $reg[1];
3905 if (empty($conf->$tmpmodule->dir_output)) { // modulepart not supported
3906 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3907 exit;
3908 }
3909
3910 // Check fuser->rights->modulepart->myobject->read and fuser->rights->modulepart->read
3911 $partsofdirinoriginalfile = explode('/', $original_file);
3912 if (!empty($partsofdirinoriginalfile[1])) { // If original_file is xxx/filename (xxx is a part we will use)
3913 $partofdirinoriginalfile = $partsofdirinoriginalfile[0];
3914 if (($partofdirinoriginalfile && $fuser->hasRight($tmpmodule, $partofdirinoriginalfile, 'read')) || preg_match('/^specimen/i', $original_file)) {
3915 $accessallowed = 1;
3916 }
3917 }
3918 if ($fuser->hasRight($tmpmodule, $read) || preg_match('/^specimen/i', $original_file)) {
3919 $accessallowed = 1;
3920 }
3921 $original_file = $conf->$tmpmodule->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3922 } else {
3923 // Main generic case
3924 if (empty($conf->$modulepart->dir_output)) { // modulepart not supported
3925 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.'). The module for this modulepart value may not be activated.');
3926 exit;
3927 }
3928
3929 // Check fuser->hasRight('modulepart', 'myobject', 'read') and fuser->hasRight('modulepart', 'read')
3930 $partsofdirinoriginalfile = explode('/', $original_file);
3931 if (!empty($partsofdirinoriginalfile[1])) { // If original_file is xxx/filename (xxx is a part we will use)
3932 $partofdirinoriginalfile = $partsofdirinoriginalfile[0];
3933 if ($partofdirinoriginalfile && ($fuser->hasRight($modulepart, $partofdirinoriginalfile, 'lire') || $fuser->hasRight($modulepart, $partofdirinoriginalfile, 'read'))) {
3934 $accessallowed = 1;
3935 }
3936 }
3937 if (($fuser->hasRight($modulepart, $lire) || $fuser->hasRight($modulepart, $read)) || ($fuser->hasRight($modulepart, 'all', $lire) || $fuser->hasRight($modulepart, 'all', $read))) {
3938 $accessallowed = 1;
3939 }
3940
3941 $subdir = '';
3942 $regs = array();
3943 if (preg_match('/^(\w+)@(\w+)$/', $originalmodulepart, $regs)) {
3944 $subdir = $regs[1].'/';
3945 }
3946
3947 if (is_array($conf->$modulepart->multidir_output) && !empty($conf->$modulepart->multidir_output[$entity])) {
3948 $original_file = $conf->$modulepart->multidir_output[$entity].'/'.$subdir.$original_file;
3949 } else {
3950 $original_file = $conf->$modulepart->dir_output.'/'.$subdir.$original_file;
3951 }
3952
3953 // TODO
3954 // Implement a way for the generic case to set $sqlprotectagainstexternals automatically.
3955 // For the moment, external modules can use the following hook checkSecureAccess if they need to protect against external users.
3956 }
3957
3958 $parameters = array(
3959 'modulepart' => $modulepart,
3960 'original_file' => $original_file,
3961 'entity' => $entity,
3962 'fuser' => $fuser,
3963 'refname' => '',
3964 'mode' => $mode
3965 );
3966 $reshook = $hookmanager->executeHooks('checkSecureAccess', $parameters, $object);
3967 if ($reshook > 0) {
3968 if (!empty($hookmanager->resArray['original_file'])) {
3969 $original_file = $hookmanager->resArray['original_file'];
3970 }
3971 if (!empty($hookmanager->resArray['accessallowed'])) {
3972 $accessallowed = $hookmanager->resArray['accessallowed'];
3973 }
3974 if (!empty($hookmanager->resArray['sqlprotectagainstexternals'])) {
3975 $sqlprotectagainstexternals = $hookmanager->resArray['sqlprotectagainstexternals'];
3976 }
3977 }
3978 }
3979
3980 $ret = array(
3981 'accessallowed' => ($accessallowed ? 1 : 0),
3982 'sqlprotectagainstexternals' => $sqlprotectagainstexternals,
3983 'original_file' => $original_file
3984 );
3985
3986 return $ret;
3987}
3988
3997function dol_filecache($directory, $filename, $object)
3998{
3999 if (!dol_is_dir($directory)) {
4000 $result = dol_mkdir($directory);
4001 if ($result < -1) {
4002 dol_syslog("Failed to create the cache directory ".$directory, LOG_WARNING);
4003 }
4004 }
4005 $cachefile = $directory.$filename;
4006
4007 file_put_contents($cachefile, json_encode($object), LOCK_EX);
4008 dolChmod($cachefile);
4009}
4010
4019function dol_cache_refresh($directory, $filename, $cachetime)
4020{
4021 $now = dol_now();
4022 $cachefile = $directory.$filename;
4023 $refresh = !file_exists($cachefile) || ($now - $cachetime) > dol_filemtime($cachefile);
4024 return $refresh;
4025}
4026
4034function dol_readcachefile($directory, $filename)
4035{
4036 $cachefile = $directory.$filename;
4037 $object = json_decode(file_get_contents($cachefile));
4038 return $object;
4039}
4040
4047function dirbasename($pathfile)
4048{
4049 return preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'\//', '', $pathfile);
4050}
4051
4052
4064function getFilesUpdated(&$file_list, SimpleXMLElement $dir, $path = '', $pathref = '', &$checksumconcat = array())
4065{
4066 global $conffile;
4067
4068 //$exclude = 'install';
4069
4070 $entry = array();
4071 $algo = '';
4072 if (!empty($dir->md5file)) {
4073 $entry = $dir->md5file;
4074 $algo = 'md5';
4075 } elseif (!empty($dir->sha256file)) {
4076 $entry = $dir->sha256file;
4077 $algo = 'sha256';
4078 }
4079
4080 foreach ($entry as $file) { // $file is a simpleXMLElement
4081 $filename = $path.$file['name'];
4082 $file_list['insignature'][] = $filename;
4083 $expectedsize = (empty($file['size']) ? '' : $file['size']);
4084 $expectedhash = (string) $file;
4085
4086 if (!file_exists($pathref.'/'.$filename)) {
4087 $file_list['missing'][] = array('filename' => $filename, 'expectedhash' => $expectedhash, 'expectedsize' => $expectedsize, 'algo' => (string) $algo);
4088 } else {
4089 $hash_local = hash_file($algo, $pathref.'/'.$filename);
4090
4091 if ($conffile == '/etc/dolibarr/conf.php' && $filename == '/filefunc.inc.php') { // For install with deb or rpm, we ignore test on filefunc.inc.php that was modified by package
4092 $checksumconcat[] = $expectedhash;
4093 } else {
4094 if ($hash_local != $expectedhash) {
4095 $file_list['updated'][] = array('filename' => $filename, 'expectedhash' => $expectedhash, 'expectedsize' => $expectedsize, 'hash' => (string) $hash_local, 'algo' => (string) $algo);
4096 }
4097 $checksumconcat[] = $hash_local;
4098 }
4099 }
4100 }
4101
4102 foreach ($dir->dir as $subdir) { // $subdir['name'] is '' or '/accountancy/admin' for example
4103 getFilesUpdated($file_list, $subdir, $path.$subdir['name'].'/', $pathref, $checksumconcat);
4104 }
4105
4106 return $file_list;
4107}
4108
4116function dragAndDropFileUpload($htmlname)
4117{
4118 global $object, $langs;
4119
4120 // Every generated javascript string that carries an interpolated value is delimited by a single quote, so
4121 // dol_escape_js() is called with the mode 1 everywhere below: it escapes a single quote and leaves a double
4122 // quote alone. The default mode would rewrite a double quote into an escaped single quote, which is safe
4123 // inside a '...' string but silently alters the value.
4124 // dol_escape_js() escapes the quotes but not '</', and PHP_SELF holds the path info of the request on a
4125 // server that accepts it, so a request could close the script tag below and open one of its own.
4126 $pageurl = str_replace('</', '<\\/', dol_escape_js($_SERVER["PHP_SELF"], 1));
4127
4128 // Values interpolated into the heredoc below via {$...}: dol_escape_js() is still called with
4129 // mode 1 on each of them for the same reason as $pageurl above (they are embedded into JS '...' strings).
4130 $nonce = getNonce();
4131 $fkElement = dol_escape_js((string) $object->id, 1);
4132 $elementType = dol_escape_js($object->element, 1);
4133 $token = currentToken();
4134 $ajaxUrl = DOL_URL_ROOT.'/core/ajax/fileupload.php';
4135
4136 $out = "";
4137 $out .= '<div id="'.$htmlname.'Message" class="dragDropAreaMessage hidden"><span>'.img_picto("", 'download').'<br>'.$langs->trans("DropFileToAddItToObject").'</span></div>';
4138 $out .= "\n<!-- JS CODE TO ENABLE DRAG AND DROP OF FILE -->\n";
4139 $out .= <<<JS
4140<script nonce="{$nonce}">
4141 jQuery(document).ready(function() {
4142 var enterTargetDragDrop = null;
4143
4144 $('#{$htmlname}').addClass('cssDragDropArea');
4145
4146 $(".cssDragDropArea").on("dragenter", function(ev, ui) {
4147 var dataTransfer = ev.originalEvent.dataTransfer;
4148 var dataTypes = dataTransfer.types;
4149 // console.log(dataTransfer);
4150 // console.log(dataTypes);
4151
4152 if (!dataTypes || ($.inArray('Files', dataTypes) === -1)) {
4153 // The element dragged is not a file, so we avoid the "dragenter"
4154 ev.preventDefault();
4155 return false;
4156 }
4157
4158 // Entering drop area. Highlight area
4159 console.log("dragAndDropFileUpload: We add class highlightDragDropArea")
4160 enterTargetDragDrop = ev.target;
4161 $(this).addClass("highlightDragDropArea");
4162 $('#{$htmlname}Message').removeClass('hidden');
4163 ev.preventDefault();
4164 });
4165
4166 $(".cssDragDropArea").on("dragleave", function(ev) {
4167 // Going out of drop area. Remove Highlight
4168 if (enterTargetDragDrop == ev.target){
4169 console.log("dragAndDropFileUpload: We remove class highlightDragDropArea")
4170 $('#{$htmlname}Message').addClass('hidden');
4171 $(this).removeClass("highlightDragDropArea");
4172 }
4173 });
4174
4175 $(".cssDragDropArea").on("dragover", function(ev) {
4176 ev.preventDefault();
4177 return false;
4178 });
4179
4180 $(".cssDragDropArea").on("drop", function(e) {
4181 console.log('Trigger event file dropped. fk_element={$fkElement} element={$elementType}');
4182 e.preventDefault();
4183 fd = new FormData();
4184 fd.append('fk_element', '{$fkElement}');
4185 fd.append('element', '{$elementType}');
4186 fd.append('token', '{$token}');
4187 fd.append("action", "linkit");
4188
4189 var dataTransfer = e.originalEvent.dataTransfer;
4190
4191 if (dataTransfer.files && dataTransfer.files.length){
4192 var droppedFiles = e.originalEvent.dataTransfer.files;
4193 $.each(droppedFiles, function(index,file){
4194 fd.append("files[]", file,file.name)
4195 });
4196 }
4197 $(".cssDragDropArea").removeClass("highlightDragDropArea");
4198 counterdragdrop = 0;
4199 $.ajax({
4200 url: '{$ajaxUrl}',
4201 type: "POST",
4202 processData: false,
4203 contentType: false,
4204 data: fd,
4205 success:function() {
4206 console.log("Uploaded.", arguments);
4207 /* arguments[0] is the json string of files */
4208 /* arguments[1] is the value for variable "success", can be 0 or 1 */
4209 let listoffiles = [];
4210 /* The answer is not the expected json when php stopped before answering, for example when
4211 post_max_size was reached. Without this, the exception of JSON.parse() would leave the
4212 user on a page with no message at all, thinking the file was added. */
4213 try {
4214 listoffiles = JSON.parse(arguments[0]);
4215 } catch (e) {
4216 window.location.href = '{$pageurl}?id={$fkElement}&seteventmessages=ErrorUploadFileDragDrop:errors';
4217 return;
4218 }
4219 console.log(listoffiles);
4220 let nboferror = 0;
4221 for (let i = 0; i < listoffiles.length; i++) {
4222 console.log(listoffiles[i].error);
4223 if (listoffiles[i].error) {
4224 nboferror++;
4225 }
4226 }
4227 console.log(nboferror);
4228 /* An empty list means no file was stored at all, so it is an error and not a success:
4229 php empties \$_FILES when post_max_size is reached. */
4230 if (listoffiles.length == 0) {
4231 window.location.href = '{$pageurl}?id={$fkElement}&seteventmessages=ErrorUploadFileDragDrop:errors';
4232 } else if (nboferror > 0) {
4233 window.location.href = '{$pageurl}?id={$fkElement}&seteventmessages=ErrorOnAtLeastOneFileUpload:warnings';
4234 } else {
4235 window.location.href = '{$pageurl}?id={$fkElement}&seteventmessages=UploadFileDragDropSuccess:mesgs';
4236 }
4237 },
4238 error:function(jqXHR) {
4239 console.log("Error Uploading.", arguments)
4240 if (jqXHR.status == 403) {
4241 window.location.href = '{$pageurl}?id={$fkElement}&seteventmessages=ErrorUploadFileDragDropPermissionDenied:errors';
4242 } else {
4243 window.location.href = '{$pageurl}?id={$fkElement}&seteventmessages=ErrorUploadFileDragDrop:errors';
4244 }
4245 },
4246 })
4247 });
4248 });
4249</script>
4250
4251JS;
4252 return $out;
4253}
4254
4265function archiveOrBackupFile($srcfile, $max_versions = 5, $archivedir = '', $suffix = "v", $moveorcopy = 'move')
4266{
4267 $base_file_pattern = ($archivedir ? $archivedir : dirname($srcfile)).'/'.basename($srcfile).".".$suffix;
4268 $files_in_directory = glob($base_file_pattern . "*");
4269
4270 // Extract the modification timestamps for each file
4271 $files_with_timestamps = [];
4272 foreach ($files_in_directory as $file) {
4273 $files_with_timestamps[] = [
4274 'file' => $file,
4275 'timestamp' => filemtime($file)
4276 ];
4277 }
4278
4279 // Sort the files by modification date
4280 $sorted_files = [];
4281 while (count($files_with_timestamps) > 0) {
4282 $latest_file = null;
4283 $latest_index = null;
4284
4285 // Find the latest file by timestamp
4286 foreach ($files_with_timestamps as $index => $file_info) {
4287 if ($latest_file === null || (is_array($latest_file) && $file_info['timestamp'] > $latest_file['timestamp'])) {
4288 $latest_file = $file_info;
4289 $latest_index = $index;
4290 }
4291 }
4292
4293 // Add the latest file to the sorted list and remove it from the original list
4294 if ($latest_file !== null) {
4295 $sorted_files[] = $latest_file['file'];
4296 unset($files_with_timestamps[$latest_index]);
4297 }
4298 }
4299
4300 // Delete the oldest files to keep only the allowed number of versions
4301 if (count($sorted_files) >= $max_versions) {
4302 $oldest_files = array_slice($sorted_files, $max_versions - 1);
4303 foreach ($oldest_files as $oldest_file) {
4304 dol_delete_file($oldest_file, 0, 0, 0, null, false, 0);
4305 }
4306 }
4307
4308 $timestamp = dol_now('gmt');
4309 $new_backup = $srcfile . ".v" . $timestamp;
4310
4311 // Move or copy the original file to the new backup with the timestamp
4312 if ($moveorcopy == 'move') {
4313 $result = dol_move($srcfile, $new_backup, '0', 1, 0, 0);
4314 } else {
4315 $result = dol_copy($srcfile, $new_backup, '0', 1, 0, 0);
4316 }
4317
4318 if (!$result) {
4319 return false;
4320 }
4321
4322 return true;
4323}
4324
4331function dolDocToText($filetoprocess, $useFullTextIndexation = 'pdftotext', $options = 'html')
4332{
4333 global $conf, $db, $user;
4334
4335 $error = 0;
4336 $keywords = array();
4337 $textforfulltextindex = '';
4338 $cmd = '';
4339 $message = '';
4340
4341 if (empty($useFullTextIndexation)) {
4342 $useFullTextIndexation = 'pdftotext';
4343 }
4344
4345 // TODO Move this into external submodule files
4346
4347 // TODO Develop a native PHP parser using sample code in https://github.com/adeel/php-pdf-parser or https://github.com/smalot/pdfparser
4348 // Use the method pdftotext to generate a HTML
4349 if (preg_match('/pdftotext/i', $useFullTextIndexation)) {
4350 include_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
4351 $utils = new Utils($db);
4352 $outputfile = $conf->admin->dir_temp.'/tmppdftotext.'.$user->id.'.out'; // File used with popen method
4353
4354 // We also exclude '/temp/' dir and 'documents/admin/documents'
4355 // We make escapement here and call executeCLI without escapement because we don't want to have the '*.log' escaped.
4356 if ($options == 'fulltext') {
4357 $params = '-nodiag -layout';
4358 } else {
4359 $params = '-htmlmeta';
4360 }
4361
4362 // MAIN_SAVE_FILE_CONTENT_AS_TEXT_PDFTOTEXT can be for example: "/usr/bin/pdftotext"
4363 // It is for the moment a hidden constant.
4364 $cmd = escapeshellcmd(dol_sanitizePathName(getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_PDFTOTEXT', 'pdftotext'))) . " " . $params ." '".escapeshellcmd($filetoprocess)."' - ";
4365 $resultexec = $utils->executeCLI($cmd, $outputfile, 0, null, 1);
4366
4367 if (empty($resultexec['error'])) {
4368 $matches = array();
4369 if ($options == 'fulltext') {
4370 $textforfulltextindex = $resultexec['output'];
4371 }
4372 if ($options == 'html') {
4373 $txt = $resultexec['output'];
4374 if (preg_match('/<meta name="keywords" content="([^\/]+)"\s*\/>/i', $txt, $matches)) {
4375 $keywords = $matches[1];
4376 }
4377 if (preg_match('/<pre>(.*)<\/pre>/si', $txt, $matches)) {
4378 $textforfulltextindex = dol_string_nounprintableascii($matches[1], 0);
4379 }
4380 }
4381 } else {
4382 $message .= $resultexec['output'];
4383 $message .= ($message ? "\n" : "").$resultexec['error'];
4384 dol_syslog($resultexec['error']);
4385 $error++;
4386 }
4387 }
4388
4389
4390 // Use the method docling to generate a .md (https://ds4sd.github.io/docling/)
4391 if (preg_match('/docling/i', $useFullTextIndexation)) {
4392 include_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
4393 $utils = new Utils($db);
4394 $outputfile = $conf->admin->dir_temp.'/tmpdocling.'.$user->id.'.out'; // File used with popen method
4395
4396 // We also exclude '/temp/' dir and 'documents/admin/documents'
4397 // We make escapement here and call executeCLI without escapement because we don't want to have the '*.log' escaped.
4398 // MAIN_SAVE_FILE_CONTENT_AS_TEXT_DOCLING can be for example: "/usr/bin/docling"
4399 $cmd = escapeshellcmd(dol_sanitizePathName(getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_DOCLING', 'docling')))." --from pdf --to text '".escapeshellcmd($filetoprocess)."'";
4400 $resultexec = $utils->executeCLI($cmd, $outputfile, 0, null, 1);
4401
4402 if (!$resultexec['error']) {
4403 $txt = $resultexec['output'];
4404 //$matches = array();
4405 //if (preg_match('/<meta name="Keywords" content="([^\/]+)"\s*\/>/i', $txt, $matches)) {
4406 // $keywords = $matches[1];
4407 //}
4408 //if (preg_match('/<pre>(.*)<\/pre>/si', $txt, $matches)) {
4409 // $textforfulltextindex = dol_string_nounprintableascii($matches[1], 0);
4410 //}
4411 $textforfulltextindex = $txt;
4412 } else {
4413 $message .= $resultexec['output'];
4414 $message .= ($message ? "\n" : "").$resultexec['error'];
4415 dol_syslog($resultexec['error']);
4416 $error++;
4417 }
4418 }
4419
4420 return array('error' => $error, 'message' => $message, 'keywords' => $keywords, 'content' => $textforfulltextindex, 'cmd' => $cmd);
4421}
4422
4429function removeLastLine($fullpath)
4430{
4431 // Generate tmp file content without the last line
4432 $fp = fopen($fullpath, "r");
4433 fseek($fp, -1, SEEK_END);
4434 $pos = -1;
4435 $char = fgetc($fp);
4436 while ($char === "\n" || $char === "\r") { // Go to last real char of last line
4437 fseek($fp, $pos--, SEEK_END);
4438 $char = fgetc($fp);
4439 }
4440 while ($char !== "\n" && $char !== false) {
4441 fseek($fp, $pos--, SEEK_END);
4442 $char = fgetc($fp);
4443 }
4444 /*
4445 while ($char === "\n" || $char === "\r") { // Go to last real char of last-1 line
4446 fseek($fp, $pos--, SEEK_END);
4447 $char = fgetc($fp);
4448 }
4449 */
4450 $truncatePos = ftell($fp);
4451 fclose($fp);
4452 // Truncate the tmp file to remove the last line
4453 $fp = fopen($fullpath, "c+");
4454 ftruncate($fp, $truncatePos);
4455 fclose($fp);
4456
4457 return 1;
4458}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$propal type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition propal.php:280
Class to manage agenda events (actions)
Class to scan for virus.
Class to manage ECM files.
Class to manage Trips and Expenses.
Class to manage a HTML form to send a unitary email Usage: $formail = new FormMail($db) $formmail->pr...
Class of the module paid holiday.
Class to manage hooks.
Class to manage projects.
Class to manage salary payments.
Class to manage tasks.
Class to manage Dolibarr users.
Class to manage utility methods.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
$conffile
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
dol_most_recent_file($dir, $regexfilter='', $excludefilter=array('(\.meta|_preview.*\.png) $', '^\.'), $nohook=0, $mode=0, $limit=0)
Return file(s) into a directory (by default most recent)
dol_move($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array(), $entity=null)
Move a file into another name.
dol_dir_list_in_database($path, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $sqlfilters="", $object=null)
Scan a directory and return a list of files/directories.
dol_is_link($pathoffile)
Return if path is a symbolic link.
dol_compare_file($a, $b)
Fast compare of 2 files identified by their properties ->name, ->date and ->size.
removePatternFromFile(string $filePath, string $pattern)
Removes content from a file that matches a given pattern.
dol_meta_create($object)
Create a meta file with document file into same directory.
dol_is_url($uri)
Return if path is an URI (the name of the method is misleading).
dol_basename($pathfile)
Make a basename working with all page code (default PHP basenamed fails with cyrillic).
Definition files.lib.php:40
getFilesUpdated(&$file_list, SimpleXMLElement $dir, $path='', $pathref='', &$checksumconcat=array())
Function to get list of updated or modified files.
dol_filemtime($pathoffile)
Return time of a file.
dol_filesize($pathoffile)
Return size of a file.
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
dol_add_file_process($upload_dir, $allowoverwrite=0, $updatesessionordb=0, $keyforsourcefile='addedfile', $savingdocmask='', $link=null, $trackid='', $generatethumbs=1, $object=null, $forceFullTextIndexation='', $mode=0)
Get and save an upload file (for example after submitting a new file in a mail form).
completeFileArrayWithDatabaseInfo(&$filearray, $relativedir, $object=null)
Complete $filearray with data from database.
archiveOrBackupFile($srcfile, $max_versions=5, $archivedir='', $suffix="v", $moveorcopy='move')
Manage backup versions for a given file, ensuring only a maximum number of versions are kept.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_move_dir($srcdir, $destdir, $overwriteifexists=1, $indexdatabase=1, $renamedircontent=1)
Move a directory into another name.
addFileIntoDatabaseIndex($dir, $file, $fullpathorig='', $mode='uploaded', $setsharekey=0, $object=null, $forceFullTextIndexation='')
Add a file into database index.
dol_fileperm($pathoffile)
Return permissions of a file.
dol_is_writable($folderorfile)
Test if directory or filename is writable.
dol_delete_dir($dir, $nophperrors=0)
Remove a directory (not recursive, so content must be empty).
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
isRealPdf(string $filePath)
Check if a file is a real PDF file by checking its signature and its MIME type.
dol_uncompress($inputfile, $outputdir)
Uncompress a file.
dol_check_secure_access_document($modulepart, $original_file, $entity, $fuser=null, $refname='', $mode='read')
Security check when accessing to a document (used by document.php, viewimage.php and webservices to g...
dol_init_file_process($pathtoscan='', $trackid='')
Scan a directory and init $_SESSION to manage uploaded files with list of all found files.
dol_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert a PDF file into another image format.
removeLastLine($fullpath)
Remove the last line of a text file.
dol_filecache($directory, $filename, $object)
Store object in file.
dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists, $arrayreplacement=null, $excludesubdir=0, $excludefileext=null, $excludearchivefiles=0)
Copy a dir to another dir.
dragAndDropFileUpload($htmlname)
Function to manage the drag and drop of a file.
dol_is_file($pathoffile)
Return if path is a file.
dol_count_nb_of_line($file)
Count number of lines in a file.
dolCheckVirus($src_file, $dest_file='')
Check virus into a file.
dol_unescapefile($filename)
Unescape a file submitted by upload.
dolDocToText($filetoprocess, $useFullTextIndexation='pdftotext', $options='html')
dol_dir_is_emtpy($folder)
Test if a folder is empty.
dol_remove_file_process($filenb, $donotupdatesession=0, $donotdeletefile=1, $trackid='')
Remove an uploaded file (for example after submitting a new file a mail form).
dolCheckOnFileName($src_file, $dest_file='')
Check virus into a file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:65
dol_readcachefile($directory, $filename)
Read object from cachefile.
dol_is_dir($folder)
Test if filename is a directory.
dol_cache_refresh($directory, $filename, $cachetime)
Test if Refresh needed.
dolReplaceInFile($srcfile, $arrayreplacement, $destfile='', $newmask='0', $indexdatabase=0, $arrayreplacementisregex=0)
Make replacement of strings into a file.
dol_delete_preview($object)
Delete all preview files linked to object instance.
dol_is_dir_empty($dir)
Return if path is empty.
dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan=0, $uploaderrorcode=0, $nohook=0, $keyforsourcefile='addedfile', $upload_dir='', $mode=0)
Check validity of a file upload from an GUI page, and move it to its final destination.
deleteFilesIntoDatabaseIndex($dir, $file, $mode='uploaded', $object=null)
Delete files into database index using search criteria.
dol_now($mode='gmt')
Return date for now.
getExecutableContent()
Return array of extension for executable files of text files that can contains executable code.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
getDolUserInt($key, $default=0, $tmpuser=null)
Return Dolibarr user constant int value.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_sanitizePathName($str, $newstr='_', $unaccent=0, $allowdash=0)
Clean a string to use it as a path name.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0, $forbiddenfields=array())
forgeSQLFromUniversalSearchCriteria
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
isAFileWithExecutableContent($filename)
Return if a file can contains executable content.
getNonce()
Return a random string to be used as a nonce value for js.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
vignette($file, $maxWidth=160, $maxHeight=120, $extName='_small', $quality=50, $outdir='thumbs', $targetformat=0)
Create a thumbnail from an image file (Supported extensions are gif, jpg, png and bmp).
if(!defined( 'IMAGETYPE_WEBP')) getDefaultImageSizes()
Return default values for image sizes.
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
getRandomPassword($generic=false, $replaceambiguouschars=null, $length=32)
Return a generated password using default module.
checkUserAccessToObject($user, array $featuresarray, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='', $dbt_select='rowid', $parenttableforentity='')
Check that access by a given user to an object is ok.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.