dolibarr 22.0.5
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-2024 Frédéric France <frederic.france@free.fr>
8 * Copyright (C) 2023 Lenin Rivas <lenin.rivas777@gmail.com>
9 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 * or see https://www.gnu.org/
24 */
25
38function dol_basename($pathfile)
39{
40 return preg_replace('/^.*\/([^\/]+)$/', '$1', rtrim($pathfile, '/'));
41}
42
63function 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)
64{
65 global $hookmanager;
66 global $object;
67
68 if ($recursive <= 1) { // Avoid too verbose log
69 $error_info = "";
70
71 // Verify filters (only on the first call of the function)
72 $filter_ok = true;
73 if (!is_array($filter)) {
74 if (strlen($filter) > 25000) { // Note that limit depends on syntax of filter
75 dol_syslog("Value for filter is too large", LOG_ERR);
76 $filter_ok = false;
77 } else {
78 // Check that all '/' are escaped.
79 if ((int) preg_match('/(?:^|[^\\\\])\//', $filter) > 0) {
80 $excludefilter_ok = false;
81 $error_info .= " error='filter_has_unescaped_slash'";
82 dol_syslog("'$filter' has unescaped '/'", LOG_ERR);
83 }
84 }
85 }
86
87 // Ensure we have an array for the exclusions
88 $excludefilter_ok = true;
89 $exclude_array = ($excludefilter === null || $excludefilter === '') ? array() : (is_array($excludefilter) ? $excludefilter : array($excludefilter));
90 foreach ($exclude_array as $f) {
91 // Check that all '/' are escaped.
92 if ((int) preg_match('/(?:^|[^\\\\])\//', $f) > 0) {
93 $excludefilter_ok = false;
94 $error_info .= " error='excludefilter_has_unescaped_slash'";
95 dol_syslog("'$f' has unescaped '/'", LOG_ERR);
96 }
97 }
98
99 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);
100 // print 'xxx'."files.lib.php::dol_dir_list path=".$utf8_path." types=".$types." recursive=".$recursive." filter=".json_encode($filter)." excludefilter=".json_encode($exclude_array);
101 if (!$filter_ok || !$excludefilter_ok) {
102 // Return empty array when filters are invalid
103 return array();
104 }
105 } else {
106 // Already computed before
107 $exclude_array = ($excludefilter === null || $excludefilter === '') ? array() : (is_array($excludefilter) ? $excludefilter : array($excludefilter));
108 }
109
110 // Define excludefilterarray (before while, for speed)
111 $excludefilterarray = array_merge(array('^\.'), $exclude_array);
112
113 $loaddate = ($mode == 1 || $mode == 2 || $nbsecondsold != 0 || $sortcriteria == 'date');
114 $loadsize = ($mode == 1 || $mode == 3 || $sortcriteria == 'size');
115 $loadperm = ($mode == 1 || $mode == 4 || $sortcriteria == 'perm');
116
117 $now = dol_now();
118 $reshook = 0;
119 $file_list = array();
120
121 // Clean parameters
122 $utf8_path = preg_replace('/([\\/]+)$/', '', $utf8_path);
123
124 if (preg_match('/\*/', $utf8_path)) {
125 $utf8_path_array = glob($utf8_path, GLOB_ONLYDIR); // This scan dir for files. If file does not exists, return empty.
126 //$os_path_array = dol_dir_list($utf8_path);
127 } else {
128 $utf8_path_array = array($utf8_path);
129 }
130
131 foreach ($utf8_path_array as $utf8_path_cursor) {
132 $os_path = dol_osencode($utf8_path_cursor);
133 if (!$nohook && $hookmanager instanceof HookManager) {
134 $hookmanager->resArray = array();
135
136 $hookmanager->initHooks(array('fileslib'));
137
138 $parameters = array(
139 'path' => $os_path,
140 'types' => $types,
141 'recursive' => $recursive,
142 'filter' => $filter,
143 'excludefilter' => $exclude_array, // Already converted to array.
144 'sortcriteria' => $sortcriteria,
145 'sortorder' => $sortorder,
146 'loaddate' => $loaddate,
147 'loadsize' => $loadsize,
148 'mode' => $mode
149 );
150 $reshook = $hookmanager->executeHooks('getDirList', $parameters, $object);
151 }
152
153 // $hookmanager->resArray may contain array stacked by other modules
154 if (empty($reshook)) {
155 if (!is_dir($os_path)) {
156 continue;
157 }
158
159 if (($dir = opendir($os_path)) === false) {
160 continue;
161 }
162
163 $filedate = '';
164 $filesize = '';
165 $fileperm = '';
166
167 while (false !== ($os_file = readdir($dir))) { // $utf8_file is always a basename (in directory $os_path)
168 $os_fullpathfile = ($os_path ? $os_path.'/' : '').$os_file;
169
170 if (!utf8_check($os_file)) {
171 $utf8_file = mb_convert_encoding($os_file, 'UTF-8', 'ISO-8859-1'); // Make sure data is stored in utf8 in memory
172 } else {
173 $utf8_file = $os_file;
174 }
175
176 $utf8_fullpathfile = $utf8_path_cursor."/".$utf8_file; // Temp variable for speed
177
178 // Check if file is qualified
179 $qualified = 1;
180 foreach ($excludefilterarray as $filt) {
181 if (preg_match('/'.$filt.'/i', $utf8_file) || preg_match('/'.$filt.'/i', $utf8_fullpathfile)) {
182 $qualified = 0;
183 break;
184 }
185 }
186 //print $utf8_fullpathfile.' '.$utf8_file.' '.$qualified.'<br>';
187
188 if ($qualified) {
189 $isdir = is_dir($os_fullpathfile);
190 // Check whether this is a file or directory and whether we're interested in that type
191 if ($isdir) {
192 // Add entry into file_list array
193 if (($types == "directories") || ($types == "all")) {
194 if ($loaddate || $sortcriteria == 'date') {
195 $filedate = dol_filemtime($utf8_fullpathfile);
196 }
197 if ($loadsize || $sortcriteria == 'size') {
198 $filesize = dol_filesize($utf8_fullpathfile);
199 }
200 if ($loadperm || $sortcriteria == 'perm') {
201 $fileperm = dol_fileperm($utf8_fullpathfile);
202 }
203
204 $qualifiedforfilter = 0;
205 if (empty($filter)) {
206 $qualifiedforfilter = 1;
207 } else {
208 $testpregmatch = false;
209 if (is_array($filter)) {
210 $chunks = array_chunk($filter, 500);
211 foreach ($chunks as $chunk) {
212 $testpregmatch = preg_match('/'.implode('|', $chunk).'/i', $utf8_file); // May failed if $filter too large
213 if ($testpregmatch) {
214 break;
215 }
216 }
217 } else {
218 $testpregmatch = preg_match('/'.$filter.'/i', $utf8_file); // May failed if $filter too large
219 }
220 if ($testpregmatch) {
221 $qualifiedforfilter = 1;
222 }
223 }
224
225 if ($qualifiedforfilter) { // We do not search key $filter into all $path, only into $file part
226 $reg = array();
227 preg_match('/([^\/]+)\/[^\/]+$/', $utf8_fullpathfile, $reg);
228 $level1name = (isset($reg[1]) ? $reg[1] : '');
229 $file_list[] = array(
230 "name" => $utf8_file,
231 "path" => $utf8_path,
232 "level1name" => $level1name,
233 "relativename" => ($relativename ? $relativename.'/' : '').$utf8_file,
234 "fullname" => $utf8_fullpathfile,
235 "date" => $filedate,
236 "size" => $filesize,
237 "perm" => $fileperm,
238 "type" => 'dir'
239 );
240 }
241 }
242
243 // if we're in a directory and we want recursive behavior, call this function again
244 if ($recursive > 0) {
245 if (empty($donotfollowsymlinks) || !is_link($os_fullpathfile)) {
246 //var_dump('eee '. $utf8_fullpathfile. ' '.is_dir($utf8_fullpathfile).' '.is_link($utf8_fullpathfile));
247 $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));
248 }
249 }
250 } elseif (in_array($types, array("files", "all"))) {
251 // Add file into file_list array
252 if ($loaddate || $sortcriteria == 'date') {
253 $filedate = dol_filemtime($utf8_fullpathfile);
254 }
255 if ($loadsize || $sortcriteria == 'size') {
256 $filesize = dol_filesize($utf8_fullpathfile);
257 }
258
259 $qualifiedforfilter = 0;
260 if (empty($filter)) {
261 $qualifiedforfilter = 1;
262 } else {
263 $testpregmatch = false;
264 if (is_array($filter)) {
265 $chunks = array_chunk($filter, 500);
266 foreach ($chunks as $chunk) {
267 $testpregmatch = preg_match('/'.implode('|', $chunk).'/i', $utf8_file); // May failed if $filter too large
268 if ($testpregmatch) {
269 break;
270 }
271 }
272 } else {
273 $testpregmatch = preg_match('/'.$filter.'/i', $utf8_file); // May failed if $filter too large
274 }
275 if ($testpregmatch) {
276 $qualifiedforfilter = 1;
277 }
278 }
279
280 if ($qualifiedforfilter) { // We do not search key $filter into all $path, only into $file part
281 if (empty($nbsecondsold) || $filedate <= ($now - $nbsecondsold)) {
282 preg_match('/([^\/]+)\/[^\/]+$/', $utf8_fullpathfile, $reg);
283 $level1name = (isset($reg[1]) ? $reg[1] : '');
284 $file_list[] = array(
285 "name" => $utf8_file,
286 "path" => $utf8_path,
287 "level1name" => $level1name,
288 "relativename" => ($relativename ? $relativename.'/' : '').$utf8_file,
289 "fullname" => $utf8_fullpathfile,
290 "date" => $filedate,
291 "size" => $filesize,
292 "type" => 'file'
293 );
294 }
295 }
296 }
297 }
298 }
299 closedir($dir);
300 }
301 }
302
303 // Obtain a list of columns
304 if (!empty($sortcriteria) && $sortorder) {
305 $file_list = dol_sort_array($file_list, $sortcriteria, ($sortorder == SORT_ASC ? 'asc' : 'desc'));
306 }
307
308 if ($hookmanager instanceof HookManager && is_array($hookmanager->resArray)) {
309 $file_list = array_merge($file_list, $hookmanager->resArray);
310 }
311
312 return $file_list;
313}
314
315
332function dol_dir_list_in_database($path, $filter = "", $excludefilter = null, $sortcriteria = "name", $sortorder = SORT_ASC, $mode = 0, $sqlfilters = "", $object = null)
333{
334 global $conf, $db;
335
336 if (is_null($object)) {
337 $object = new stdClass();
338 }
339
340 $sql = "SELECT rowid, label, entity, filename, filepath, fullpath_orig, keywords, cover, gen_or_uploaded, extraparams,";
341 $sql .= " date_c, tms as date_m, fk_user_c, fk_user_m, acl, position, share";
342 if ($mode) {
343 $sql .= ", description";
344 }
345 $sql .= " FROM ".MAIN_DB_PREFIX."ecm_files";
346 if (!empty($object->entity)) {
347 $sql .= " WHERE entity = ".((int) $object->entity);
348 } else {
349 $sql .= " WHERE entity = ".((int) $conf->entity);
350 }
351 if (preg_match('/%$/', $path)) {
352 $sql .= " AND (filepath LIKE '".$db->escape($path)."' OR filepath = '".$db->escape(preg_replace('/\/%$/', '', $path))."')";
353 } else {
354 $sql .= " AND filepath = '".$db->escape($path)."'";
355 }
356
357 // Manage filter
358 $errormessage = '';
359 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
360 if ($errormessage) {
361 dol_print_error(null, $errormessage);
362 return array();
363 }
364
365 $resql = $db->query($sql);
366 if ($resql) {
367 $file_list = array();
368 $num = $db->num_rows($resql);
369 $i = 0;
370 while ($i < $num) {
371 $obj = $db->fetch_object($resql);
372 if ($obj) {
373 $reg = array();
374 preg_match('/([^\/]+)\/[^\/]+$/', DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename, $reg);
375 $level1name = (isset($reg[1]) ? $reg[1] : '');
376 $file_list[] = array(
377 "rowid" => $obj->rowid,
378 "label" => $obj->label, // md5
379 "name" => $obj->filename,
380 "path" => DOL_DATA_ROOT.'/'.$obj->filepath,
381 "level1name" => $level1name,
382 "fullname" => DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename,
383 "fullpath_orig" => $obj->fullpath_orig,
384 "date_c" => $db->jdate($obj->date_c),
385 "date_m" => $db->jdate($obj->date_m),
386 "type" => 'file',
387 "keywords" => $obj->keywords,
388 "cover" => $obj->cover,
389 "position" => (int) $obj->position,
390 "acl" => $obj->acl,
391 "share" => $obj->share,
392 "description" => ($mode ? $obj->description : '')
393 // TODO Add 'content' with $mode == 2 ?
394 );
395 }
396 $i++;
397 }
398
399 // Obtain a list of columns
400 if (!empty($sortcriteria)) {
401 $myarray = array();
402 foreach ($file_list as $key => $row) {
403 $myarray[$key] = (isset($row[$sortcriteria]) ? $row[$sortcriteria] : '');
404 }
405 // Sort the data
406 if ($sortorder) {
407 array_multisort($myarray, $sortorder, SORT_REGULAR, $file_list);
408 }
409 }
410
411 return $file_list;
412 } else {
413 dol_print_error($db);
414 return array();
415 }
416}
417
418
428function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir, $object = null)
429{
430 global $conf, $db, $user;
431
432 if (is_null($object)) {
433 $object = new stdClass();
434 }
435
436 $filearrayindatabase = dol_dir_list_in_database(rtrim($relativedir, "/\\"), '', null, 'name', SORT_ASC, 0, '', $object);
437
438 global $modulepart;
439 if ($modulepart == 'produit' && getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
440 // TODO Remove this when PRODUCT_USE_OLD_PATH_FOR_PHOTO will be removed
441 global $object;
442 if (!empty($object->id)) {
443 if (isModEnabled("product")) {
444 $upload_dirold = $conf->product->multidir_output[$object->entity].'/'.substr(substr("000".$object->id, -2), 1, 1).'/'.substr(substr("000".$object->id, -2), 0, 1).'/'.$object->id."/photos";
445 } else {
446 $upload_dirold = $conf->service->multidir_output[$object->entity].'/'.substr(substr("000".$object->id, -2), 1, 1).'/'.substr(substr("000".$object->id, -2), 0, 1).'/'.$object->id."/photos";
447 }
448
449 $relativedirold = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dirold);
450 $relativedirold = ltrim($relativedirold, "/\\");
451
452 $filearrayindatabase = array_merge($filearrayindatabase, dol_dir_list_in_database($relativedirold, '', null, 'name', SORT_ASC));
453 }
454 } elseif ($modulepart == 'ticket') {
455 foreach ($filearray as $key => $val) {
456 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filearray[$key]['path']);
457 $rel_dir = trim($rel_dir, "/\\");
458 if ($rel_dir != $relativedir) {
459 $filearrayindatabase = array_merge($filearrayindatabase, dol_dir_list_in_database($rel_dir, '', null, 'name', SORT_ASC));
460 }
461 }
462 }
463
464 //var_dump($relativedir);
465 //var_dump($filearray);
466 //var_dump($filearrayindatabase);
467 //var_dump($object->entity);
468
469 // Complete filearray with properties found into $filearrayindatabase
470 foreach ($filearray as $key => $val) {
471 $tmpfilename = preg_replace('/\.noexe$/', '', $filearray[$key]['name']);
472 $found = 0;
473 // Search if it exists into $filearrayindatabase
474 foreach ($filearrayindatabase as $key2 => $val2) {
475 if (($filearrayindatabase[$key2]['path'] == $filearray[$key]['path']) && ($filearrayindatabase[$key2]['name'] == $tmpfilename)) {
476 $filearray[$key]['position_name'] = ($filearrayindatabase[$key2]['position'] ? $filearrayindatabase[$key2]['position'] : '0').'_'.$filearrayindatabase[$key2]['name'];
477 $filearray[$key]['position'] = $filearrayindatabase[$key2]['position'];
478 $filearray[$key]['cover'] = $filearrayindatabase[$key2]['cover'];
479 $filearray[$key]['keywords'] = $filearrayindatabase[$key2]['keywords'];
480 $filearray[$key]['acl'] = $filearrayindatabase[$key2]['acl'];
481 $filearray[$key]['rowid'] = $filearrayindatabase[$key2]['rowid'];
482 $filearray[$key]['label'] = $filearrayindatabase[$key2]['label'];
483 $filearray[$key]['share'] = $filearrayindatabase[$key2]['share'];
484 $found = 1;
485 break;
486 }
487 }
488
489 if (!$found) { // This happen in transition toward version 6, or if files were added manually into os dir.
490 $filearray[$key]['position'] = '999999'; // File not indexed are at end. So if we add a file, it will not replace an existing position
491 $filearray[$key]['cover'] = 0;
492 $filearray[$key]['acl'] = '';
493 $filearray[$key]['share'] = 0;
494
495 $rel_filename = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filearray[$key]['fullname']);
496
497 if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filename)) { // If not a tmp file
498 dol_syslog("list_of_documents We found a file called '".$filearray[$key]['name']."' not indexed into database. We add it");
499
500 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
501 $ecmfile = new EcmFiles($db);
502
503 // Add entry into database
504 $filename = basename($rel_filename);
505 $rel_dir = dirname($rel_filename);
506 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
507 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
508
509 $ecmfile->filepath = $rel_dir;
510 $ecmfile->filename = $filename;
511 $ecmfile->label = md5_file(dol_osencode($filearray[$key]['fullname'])); // $destfile is a full path to file
512 $ecmfile->fullpath_orig = $filearray[$key]['fullname'];
513 $ecmfile->gen_or_uploaded = 'unknown';
514 $ecmfile->description = ''; // indexed content
515 $ecmfile->keywords = ''; // keyword content
516 // When you scan file with dol_dir_list_in_database, you scan for files in entity of object (like with projects), even if you
517 // 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).
518 $ecmfile->entity = empty($object->entity) ? $conf->entity : $object->entity;
519
520 $result = $ecmfile->create($user);
521 if ($result < 0) {
522 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
523 } else {
524 $filearray[$key]['rowid'] = $result;
525 }
526 } else {
527 $filearray[$key]['rowid'] = 0; // Should not happened
528 }
529 }
530 }
531 //var_dump($filearray); var_dump($relativedir.' - tmpfilename='.$tmpfilename.' - found='.$found);
532}
533
534
542function dol_compare_file($a, $b)
543{
544 global $sortorder, $sortfield;
545
546 $sortorder = strtoupper($sortorder);
547
548 if ($sortorder == 'ASC') {
549 $retup = -1;
550 $retdown = 1;
551 } else {
552 $retup = 1;
553 $retdown = -1;
554 }
555
556 if ($sortfield == 'name') {
557 if ($a->name == $b->name) {
558 return 0;
559 }
560 return ($a->name < $b->name) ? $retup : $retdown;
561 }
562 if ($sortfield == 'date') {
563 if ($a->date == $b->date) {
564 return 0;
565 }
566 return ($a->date < $b->date) ? $retup : $retdown;
567 }
568 if ($sortfield == 'size') {
569 if ($a->size == $b->size) {
570 return 0;
571 }
572 return ($a->size < $b->size) ? $retup : $retdown;
573 }
574
575 return 0;
576}
577
578
585function dol_is_dir($folder)
586{
587 $newfolder = dol_osencode($folder);
588 if (is_dir($newfolder)) {
589 return true;
590 } else {
591 return false;
592 }
593}
594
601function dol_is_dir_empty($dir)
602{
603 if (!is_readable($dir)) {
604 return false;
605 }
606 return (count(scandir($dir)) == 2);
607}
608
615function dol_is_file($pathoffile)
616{
617 $newpathoffile = dol_osencode($pathoffile);
618 return is_file($newpathoffile);
619}
620
627function dol_is_link($pathoffile)
628{
629 $newpathoffile = dol_osencode($pathoffile);
630 return is_link($newpathoffile);
631}
632
639function dol_is_writable($folderorfile)
640{
641 $newfolderorfile = dol_osencode($folderorfile);
642 return is_writable($newfolderorfile);
643}
644
653function dol_is_url($uri)
654{
655 $prots = array('file', 'http', 'https', 'ftp', 'zlib', 'data', 'ssh', 'ssh2', 'ogg', 'expect');
656 return false !== preg_match('/^('.implode('|', $prots).'):/i', $uri);
657}
658
665function dol_dir_is_emtpy($folder)
666{
667 $newfolder = dol_osencode($folder);
668 if (is_dir($newfolder)) {
669 $handle = opendir($newfolder);
670 $folder_content = '';
671 $name_array = [];
672 while ((gettype($name = readdir($handle)) != "boolean")) {
673 $name_array[] = $name;
674 }
675 foreach ($name_array as $temp) {
676 $folder_content .= $temp;
677 }
678
679 closedir($handle);
680
681 if ($folder_content == "...") {
682 return true;
683 } else {
684 return false;
685 }
686 } else {
687 return true; // Dir does not exists
688 }
689}
690
698function dol_count_nb_of_line($file)
699{
700 $nb = 0;
701
702 $newfile = dol_osencode($file);
703 //print 'x'.$file;
704 $fp = fopen($newfile, 'r');
705 if ($fp) {
706 while (!feof($fp)) {
707 $line = fgets($fp);
708 // Increase count only if read was success.
709 // Test needed because feof returns true only after fgets
710 // so we do n+1 fgets for a file with n lines.
711 if ($line !== false) {
712 $nb++;
713 }
714 }
715 fclose($fp);
716 } else {
717 $nb = -1;
718 }
719
720 return $nb;
721}
722
723
731function dol_filesize($pathoffile)
732{
733 $newpathoffile = dol_osencode($pathoffile);
734 return filesize($newpathoffile);
735}
736
743function dol_filemtime($pathoffile)
744{
745 $newpathoffile = dol_osencode($pathoffile);
746 return @filemtime($newpathoffile); // @Is to avoid errors if files does not exists
747}
748
755function dol_fileperm($pathoffile)
756{
757 $newpathoffile = dol_osencode($pathoffile);
758 return fileperms($newpathoffile);
759}
760
773function dolReplaceInFile($srcfile, $arrayreplacement, $destfile = '', $newmask = '0', $indexdatabase = 0, $arrayreplacementisregex = 0)
774{
775 dol_syslog("files.lib.php::dolReplaceInFile srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." indexdatabase=".$indexdatabase." arrayreplacementisregex=".$arrayreplacementisregex);
776
777 if (empty($srcfile)) {
778 return -1;
779 }
780 if (empty($destfile)) {
781 $destfile = $srcfile;
782 }
783
784 // Clean the aa/bb/../cc into aa/cc
785 $srcfile = preg_replace('/\.\.\/?/', '', $srcfile);
786 $destfile = preg_replace('/\.\.\/?/', '', $destfile);
787
788 $destexists = dol_is_file($destfile);
789 if (($destfile != $srcfile) && $destexists) {
790 return 0;
791 }
792
793 $srcexists = dol_is_file($srcfile);
794 if (!$srcexists) {
795 dol_syslog("files.lib.php::dolReplaceInFile failed to read src file", LOG_WARNING);
796 return -3;
797 }
798
799 $tmpdestfile = $destfile.'.tmp';
800
801 $newpathofsrcfile = dol_osencode($srcfile);
802 $newpathoftmpdestfile = dol_osencode($tmpdestfile);
803 $newpathofdestfile = dol_osencode($destfile);
804 $newdirdestfile = dirname($newpathofdestfile);
805
806 if ($destexists && !is_writable($newpathofdestfile)) {
807 dol_syslog("files.lib.php::dolReplaceInFile failed Permission denied to overwrite target file", LOG_WARNING);
808 return -1;
809 }
810 if (!is_writable($newdirdestfile)) {
811 dol_syslog("files.lib.php::dolReplaceInFile failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING);
812 return -2;
813 }
814
815 dol_delete_file($tmpdestfile);
816
817 // Create $newpathoftmpdestfile from $newpathofsrcfile
818 $content = file_get_contents($newpathofsrcfile);
819
820 if (empty($arrayreplacementisregex)) {
821 $content = make_substitutions($content, $arrayreplacement, null);
822 } else {
823 foreach ($arrayreplacement as $key => $value) {
824 $content = preg_replace($key, $value, $content);
825 }
826 }
827
828 file_put_contents($newpathoftmpdestfile, $content);
829 dolChmod($newpathoftmpdestfile, $newmask);
830
831 // Rename
832 $moreinfo = array('gen_or_uploaded' => 'unknown');
833 $result = dol_move($newpathoftmpdestfile, $newpathofdestfile, $newmask, (($destfile == $srcfile) ? 1 : 0), 0, $indexdatabase, $moreinfo);
834 if (!$result) {
835 dol_syslog("files.lib.php::dolReplaceInFile failed to move tmp file to final dest", LOG_WARNING);
836 return -3;
837 }
838 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
839 $newmask = getDolGlobalString('MAIN_UMASK');
840 }
841 if (empty($newmask)) { // This should no happen
842 dol_syslog("Warning: dolReplaceInFile called with empty value for newmask and no default value defined", LOG_WARNING);
843 $newmask = '0664';
844 }
845
846 dolChmod($newpathofdestfile, $newmask);
847
848 return 1;
849}
850
851
864function dol_copy($srcfile, $destfile, $newmask = '0', $overwriteifexists = 1, $testvirus = 0, $indexdatabase = 0)
865{
866 global $db, $user;
867
868 dol_syslog("files.lib.php::dol_copy srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists);
869
870 if (empty($srcfile) || empty($destfile)) {
871 return -1;
872 }
873
874 $destexists = dol_is_file($destfile);
875 if (!$overwriteifexists && $destexists) {
876 return 0;
877 }
878
879 $newpathofsrcfile = dol_osencode($srcfile);
880 $newpathofdestfile = dol_osencode($destfile);
881 $newdirdestfile = dirname($newpathofdestfile);
882
883 if ($destexists && !is_writable($newpathofdestfile)) {
884 dol_syslog("files.lib.php::dol_copy failed Permission denied to overwrite target file", LOG_WARNING);
885 return -1;
886 }
887 if (!is_writable($newdirdestfile)) {
888 dol_syslog("files.lib.php::dol_copy failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING);
889 return -2;
890 }
891
892 // Check virus
893 $testvirusarray = array();
894 if ($testvirus) {
895 $testvirusarray = dolCheckVirus($srcfile, $destfile);
896 if (count($testvirusarray)) {
897 dol_syslog("files.lib.php::dol_copy canceled because a virus was found into source file. we ignore the copy request.", LOG_WARNING);
898 return -3;
899 }
900 }
901
902 // Copy with overwriting if exists
903 $result = @copy($newpathofsrcfile, $newpathofdestfile);
904 //$result=copy($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
905 if (!$result) {
906 dol_syslog("files.lib.php::dol_copy failed to copy", LOG_WARNING);
907 return -3;
908 }
909 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
910 $newmask = getDolGlobalString('MAIN_UMASK');
911 }
912 if (empty($newmask)) { // This should no happen
913 dol_syslog("Warning: dol_copy called with empty value for newmask and no default value defined", LOG_WARNING);
914 $newmask = '0664';
915 }
916
917 dolChmod($newpathofdestfile, $newmask);
918
919 if ($result && $indexdatabase) {
920 // Add entry into ecm database
921 $rel_filetocopyafter = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $newpathofdestfile);
922 if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetocopyafter)) { // If not a tmp file
923 $rel_filetocopyafter = preg_replace('/^[\\/]/', '', $rel_filetocopyafter);
924 //var_dump($rel_filetorenamebefore.' - '.$rel_filetocopyafter);exit;
925
926 dol_syslog("Try to copy also entries in database for: ".$rel_filetocopyafter, LOG_DEBUG);
927 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
928
929 $ecmfiletarget = new EcmFiles($db);
930 $resultecmtarget = $ecmfiletarget->fetch(0, '', $rel_filetocopyafter);
931 if ($resultecmtarget > 0) { // An entry for target name already exists for target, we delete it, a new one will be created.
932 dol_syslog("ECM dest file found, remove it", LOG_DEBUG);
933 $ecmfiletarget->delete($user);
934 } else {
935 dol_syslog("ECM dest file not found, create it", LOG_DEBUG);
936 }
937
938 $ecmSrcfile = new EcmFiles($db);
939 $resultecm = $ecmSrcfile->fetch(0, '', $srcfile);
940 if ($resultecm) {
941 dol_syslog("Fetch src file ok", LOG_DEBUG);
942 } else {
943 dol_syslog("Fetch src file error", LOG_DEBUG);
944 }
945
946 $ecmfile = new EcmFiles($db);
947 $filename = basename($rel_filetocopyafter);
948 $rel_dir = dirname($rel_filetocopyafter);
949 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
950 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
951
952 $ecmfile->filepath = $rel_dir;
953 $ecmfile->filename = $filename;
954 $ecmfile->label = md5_file(dol_osencode($destfile)); // $destfile is a full path to file
955 $ecmfile->fullpath_orig = $srcfile;
956 $ecmfile->gen_or_uploaded = 'copy';
957 $ecmfile->description = $ecmSrcfile->description;
958 $ecmfile->keywords = $ecmSrcfile->keywords;
959 $resultecm = $ecmfile->create($user);
960 if ($resultecm < 0) {
961 dol_syslog("Create ECM file ok", LOG_DEBUG);
962 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
963 } else {
964 dol_syslog("Create ECM file error", LOG_DEBUG);
965 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
966 }
967
968 if ($resultecm > 0) {
969 $result = 1;
970 } else {
971 $result = -1;
972 }
973 }
974 }
975
976 return (int) $result;
977}
978
993function dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists, $arrayreplacement = null, $excludesubdir = 0, $excludefileext = null, $excludearchivefiles = 0)
994{
995 $result = 0;
996
997 dol_syslog("files.lib.php::dolCopyDir srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists);
998
999 if (empty($srcfile) || empty($destfile)) {
1000 return -1;
1001 }
1002
1003 $destexists = dol_is_dir($destfile);
1004
1005 //if (! $overwriteifexists && $destexists) return 0; // The overwriteifexists is for files only, so propagated to dol_copy only.
1006
1007 if (!$destexists) {
1008 // We must set mask just before creating dir, because it can be set differently by dol_copy
1009 umask(0);
1010 $dirmaskdec = octdec($newmask);
1011 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
1012 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK'));
1013 }
1014 $dirmaskdec |= octdec('0200'); // Set w bit required to be able to create content for recursive subdirs files
1015
1016 $result = dol_mkdir($destfile, '', decoct($dirmaskdec));
1017
1018 if (!dol_is_dir($destfile)) {
1019 // The output directory does not exists and we failed to create it. So we stop here.
1020 return -1;
1021 }
1022 }
1023
1024 $ossrcfile = dol_osencode($srcfile);
1025 $osdestfile = dol_osencode($destfile);
1026
1027 // Recursive function to copy all subdirectories and contents:
1028 if (is_dir($ossrcfile)) {
1029 $dir_handle = opendir($ossrcfile);
1030 $tmpresult = 0; // Initialised before loop to keep old behavior, may be needed inside loop
1031 while ($file = readdir($dir_handle)) {
1032 if ($file != "." && $file != ".." && !is_link($ossrcfile."/".$file)) {
1033 if (is_dir($ossrcfile."/".$file)) {
1034 if (empty($excludesubdir) || ($excludesubdir == 2 && strlen($file) == 2)) {
1035 $newfile = $file;
1036 // Replace destination filename with a new one
1037 if (is_array($arrayreplacement)) {
1038 foreach ($arrayreplacement as $key => $val) {
1039 $newfile = str_replace($key, $val, $newfile);
1040 }
1041 }
1042 //var_dump("xxx dolCopyDir $srcfile/$file, $destfile/$file, $newmask, $overwriteifexists");
1043 $tmpresult = dolCopyDir($srcfile."/".$file, $destfile."/".$newfile, $newmask, $overwriteifexists, $arrayreplacement, $excludesubdir, $excludefileext, $excludearchivefiles);
1044 }
1045 } else {
1046 $newfile = $file;
1047
1048 if (is_array($excludefileext)) {
1049 $extension = pathinfo($file, PATHINFO_EXTENSION);
1050 if (in_array($extension, $excludefileext)) {
1051 //print "We exclude the file ".$file." because its extension is inside list ".join(', ', $excludefileext); exit;
1052 continue;
1053 }
1054 }
1055
1056 if ($excludearchivefiles == 1) {
1057 $extension = pathinfo($file, PATHINFO_EXTENSION);
1058 if (preg_match('/^[v|d]\d+$/', $extension)) {
1059 continue;
1060 }
1061 }
1062
1063 // Replace destination filename with a new one
1064 if (is_array($arrayreplacement)) {
1065 foreach ($arrayreplacement as $key => $val) {
1066 $newfile = str_replace($key, $val, $newfile);
1067 }
1068 }
1069 $tmpresult = dol_copy($srcfile."/".$file, $destfile."/".$newfile, $newmask, $overwriteifexists);
1070 }
1071 // Set result
1072 if ($result > 0 && $tmpresult >= 0) {
1073 // Do nothing, so we don't set result to 0 if tmpresult is 0 and result was success in a previous pass
1074 } else {
1075 $result = $tmpresult;
1076 }
1077 if ($result < 0) {
1078 break;
1079 }
1080 }
1081 }
1082 closedir($dir_handle);
1083 } else {
1084 // Source directory does not exists
1085 $result = -2;
1086 }
1087
1088 return (int) $result;
1089}
1090
1091
1110function dol_move($srcfile, $destfile, $newmask = '0', $overwriteifexists = 1, $testvirus = 0, $indexdatabase = 1, $moreinfo = array(), $entity = 0)
1111{
1112 global $user, $db;
1113 $result = false;
1114
1115 dol_syslog("files.lib.php::dol_move srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwritifexists=".$overwriteifexists);
1116 $srcexists = dol_is_file($srcfile);
1117 $destexists = dol_is_file($destfile);
1118
1119 if (!$srcexists) {
1120 dol_syslog("files.lib.php::dol_move srcfile does not exists. we ignore the move request.");
1121 return false;
1122 }
1123
1124 if ($overwriteifexists || !$destexists) {
1125 $newpathofsrcfile = dol_osencode($srcfile);
1126 $newpathofdestfile = dol_osencode($destfile);
1127
1128 // Check on virus
1129 $testvirusarray = array();
1130 if ($testvirus) {
1131 // Check using filename + antivirus
1132 $testvirusarray = dolCheckVirus($newpathofsrcfile, $newpathofdestfile);
1133 if (count($testvirusarray)) {
1134 dol_syslog("files.lib.php::dol_move canceled because a virus was found into source file. We ignore the move request.", LOG_WARNING);
1135 return false;
1136 }
1137 } else {
1138 // Check using filename only
1139 $testvirusarray = dolCheckOnFileName($newpathofsrcfile, $newpathofdestfile);
1140 if (count($testvirusarray)) {
1141 dol_syslog("files.lib.php::dol_move canceled because a virus was found into source file. We ignore the move request.", LOG_WARNING);
1142 return false;
1143 }
1144 }
1145
1146 global $dolibarr_main_restrict_os_commands;
1147 if (!empty($dolibarr_main_restrict_os_commands)) {
1148 $arrayofallowedcommand = explode(',', $dolibarr_main_restrict_os_commands);
1149 $arrayofallowedcommand = array_map('trim', $arrayofallowedcommand);
1150 if (in_array(basename($destfile), $arrayofallowedcommand)) {
1151 //$langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
1152 //setEventMessages($langs->trans("ErrorFilenameReserved", basename($destfile)), null, 'errors');
1153 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);
1154 return false;
1155 }
1156 }
1157
1158 $result = @rename($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
1159 if (!$result) {
1160 if ($destexists) {
1161 dol_syslog("files.lib.php::dol_move Failed. We try to delete target first and move after.", LOG_WARNING);
1162 // We force delete and try again. Rename function sometimes fails to replace dest file with some windows NTFS partitions.
1163 dol_delete_file($destfile);
1164 $result = @rename($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
1165 } else {
1166 dol_syslog("files.lib.php::dol_move Failed.", LOG_WARNING);
1167 }
1168 }
1169
1170 // Move ok
1171 if ($result && $indexdatabase) {
1172 // Rename entry into ecm database
1173 $rel_filetorenamebefore = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $srcfile);
1174 $rel_filetorenameafter = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $destfile);
1175 if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetorenameafter)) { // If not a tmp file
1176 $rel_filetorenamebefore = preg_replace('/^[\\/]/', '', $rel_filetorenamebefore);
1177 $rel_filetorenameafter = preg_replace('/^[\\/]/', '', $rel_filetorenameafter);
1178 //var_dump($rel_filetorenamebefore.' - '.$rel_filetorenameafter);exit;
1179
1180 dol_syslog("Try to rename also entries in database for full relative path before = ".$rel_filetorenamebefore." after = ".$rel_filetorenameafter, LOG_DEBUG);
1181 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1182
1183 $ecmfiletarget = new EcmFiles($db);
1184 $resultecmtarget = $ecmfiletarget->fetch(0, '', $rel_filetorenameafter, '', '', '', 0, $entity);
1185 if ($resultecmtarget > 0) { // An entry for target name already exists for target, we delete it, a new one will be created.
1186 $ecmfiletarget->delete($user);
1187 }
1188
1189 $ecmfile = new EcmFiles($db);
1190 $resultecm = $ecmfile->fetch(0, '', $rel_filetorenamebefore, '', '', '', 0, $entity);
1191 if ($resultecm > 0) { // If an entry was found for src file, we use it to move entry
1192 $filename = basename($rel_filetorenameafter);
1193 $rel_dir = dirname($rel_filetorenameafter);
1194 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
1195 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
1196
1197 $ecmfile->filepath = $rel_dir;
1198 $ecmfile->filename = $filename;
1199
1200 $resultecm = $ecmfile->update($user);
1201 } elseif ($resultecm == 0) { // If no entry were found for src files, create/update target file
1202 $filename = basename($rel_filetorenameafter);
1203 $rel_dir = dirname($rel_filetorenameafter);
1204 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
1205 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
1206
1207 $ecmfile->filepath = $rel_dir;
1208 $ecmfile->filename = $filename;
1209 $ecmfile->label = md5_file(dol_osencode($destfile)); // $destfile is a full path to file
1210 $ecmfile->fullpath_orig = basename($srcfile);
1211 if (!empty($moreinfo) && !empty($moreinfo['gen_or_uploaded'])) {
1212 $ecmfile->gen_or_uploaded = $moreinfo['gen_or_uploaded'];
1213 } else {
1214 $ecmfile->gen_or_uploaded = 'unknown'; // 'generated', 'uploaded', 'api'
1215 }
1216 if (!empty($moreinfo) && !empty($moreinfo['description'])) {
1217 $ecmfile->description = $moreinfo['description']; // indexed content
1218 } else {
1219 $ecmfile->description = ''; // indexed content
1220 }
1221 if (!empty($moreinfo) && !empty($moreinfo['keywords'])) {
1222 $ecmfile->keywords = $moreinfo['keywords']; // indexed content
1223 } else {
1224 $ecmfile->keywords = ''; // keyword content
1225 }
1226 if (!empty($moreinfo) && !empty($moreinfo['note_private'])) {
1227 $ecmfile->note_private = $moreinfo['note_private'];
1228 }
1229 if (!empty($moreinfo) && !empty($moreinfo['note_public'])) {
1230 $ecmfile->note_public = $moreinfo['note_public'];
1231 }
1232 if (!empty($moreinfo) && !empty($moreinfo['src_object_type'])) {
1233 $ecmfile->src_object_type = $moreinfo['src_object_type'];
1234 }
1235 if (!empty($moreinfo) && !empty($moreinfo['src_object_id'])) {
1236 $ecmfile->src_object_id = $moreinfo['src_object_id'];
1237 }
1238 if (!empty($moreinfo) && !empty($moreinfo['position'])) {
1239 $ecmfile->position = $moreinfo['position'];
1240 }
1241 if (!empty($moreinfo) && !empty($moreinfo['cover'])) {
1242 $ecmfile->cover = $moreinfo['cover'];
1243 }
1244 if (! empty($entity)) {
1245 $ecmfile->entity = $entity;
1246 }
1247
1248 $resultecm = $ecmfile->create($user);
1249 if ($resultecm < 0) {
1250 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1251 } else {
1252 if (!empty($moreinfo) && !empty($moreinfo['array_options']) && is_array($moreinfo['array_options'])) {
1253 $ecmfile->array_options = $moreinfo['array_options'];
1254 $resultecm = $ecmfile->insertExtraFields();
1255 if ($resultecm < 0) {
1256 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1257 }
1258 }
1259 }
1260 } elseif ($resultecm < 0) {
1261 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1262 }
1263
1264 if ($resultecm > 0) {
1265 $result = true;
1266 } else {
1267 $result = false;
1268 }
1269 }
1270 }
1271
1272 if (empty($newmask)) {
1273 $newmask = getDolGlobalString('MAIN_UMASK', '0755');
1274 }
1275
1276 // Currently method is restricted to files (dol_delete_files previously used is for files, and mask usage if for files too)
1277 // to allow mask usage for dir, we should introduce a new param "isdir" to 1 to complete newmask like this
1278 // if ($isdir) $newmaskdec |= octdec('0111'); // Set x bit required for directories
1279 dolChmod($newpathofdestfile, $newmask);
1280 }
1281
1282 return $result;
1283}
1284
1295function dol_move_dir($srcdir, $destdir, $overwriteifexists = 1, $indexdatabase = 1, $renamedircontent = 1)
1296{
1297 $result = false;
1298
1299 dol_syslog("files.lib.php::dol_move_dir srcdir=".$srcdir." destdir=".$destdir." overwritifexists=".$overwriteifexists." indexdatabase=".$indexdatabase." renamedircontent=".$renamedircontent);
1300 $srcexists = dol_is_dir($srcdir);
1301 $srcbasename = basename($srcdir);
1302 $destexists = dol_is_dir($destdir);
1303
1304 if (!$srcexists) {
1305 dol_syslog("files.lib.php::dol_move_dir srcdir does not exists. Move fails");
1306 return false;
1307 }
1308
1309 if ($overwriteifexists || !$destexists) {
1310 $newpathofsrcdir = dol_osencode($srcdir);
1311 $newpathofdestdir = dol_osencode($destdir);
1312
1313 // On windows, if destination directory exists and is empty, command fails. So if overwrite is on, we first remove destination directory.
1314 // On linux, if destination directory exists and is empty, command succeed. So no need to delete di destination directory first.
1315 // Note: If dir exists and is not empty, it will and must fail on both linux and windows even, if option $overwriteifexists is on.
1316 if ($overwriteifexists) {
1317 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1318 if (is_dir($newpathofdestdir)) {
1319 @rmdir($newpathofdestdir);
1320 }
1321 }
1322 }
1323
1324 $result = @rename($newpathofsrcdir, $newpathofdestdir);
1325
1326 // Now rename contents in the directory after the move to match the new destination
1327 if ($result && $renamedircontent) {
1328 if (file_exists($newpathofdestdir)) {
1329 $destbasename = basename($newpathofdestdir);
1330 $files = dol_dir_list($newpathofdestdir);
1331 if (!empty($files) && is_array($files)) {
1332 foreach ($files as $key => $file) {
1333 if (!file_exists($file["fullname"])) {
1334 continue;
1335 }
1336 $filepath = $file["path"];
1337 $oldname = $file["name"];
1338
1339 $newname = str_replace($srcbasename, $destbasename, $oldname);
1340 if (!empty($newname) && $newname !== $oldname) {
1341 if ($file["type"] == "dir") {
1342 $res = dol_move_dir($filepath.'/'.$oldname, $filepath.'/'.$newname, $overwriteifexists, $indexdatabase, $renamedircontent);
1343 } else {
1344 $moreinfo = array('gen_or_uploaded' => 'unknown');
1345 $res = dol_move($filepath.'/'.$oldname, $filepath.'/'.$newname, '0', $overwriteifexists, 0, $indexdatabase, $moreinfo);
1346 }
1347 if (!$res) {
1348 return $result;
1349 }
1350 }
1351 }
1352 $result = true;
1353 }
1354 }
1355 }
1356 }
1357 return $result;
1358}
1359
1367function dol_unescapefile($filename)
1368{
1369 // Remove path information and dots around the filename, to prevent uploading
1370 // into different directories or replacing hidden system files.
1371 // Also remove control characters and spaces (\x00..\x20) around the filename:
1372 return trim(basename($filename), ".\x00..\x20");
1373}
1374
1375
1383function dolCheckVirus($src_file, $dest_file = '')
1384{
1385 global $db;
1386
1387 $reterrors = dolCheckOnFileName($src_file, $dest_file);
1388 if (!empty($reterrors)) {
1389 return $reterrors;
1390 }
1391
1392 if (getDolGlobalString('MAIN_ANTIVIRUS_COMMAND')) {
1393 if (!class_exists('AntiVir')) {
1394 require_once DOL_DOCUMENT_ROOT.'/core/class/antivir.class.php';
1395 }
1396 $antivir = new AntiVir($db);
1397 $result = $antivir->dol_avscan_file($src_file);
1398 if ($result < 0) { // If virus or error, we stop here
1399 $reterrors = $antivir->errors;
1400 return $reterrors;
1401 }
1402 }
1403 return array();
1404}
1405
1413function dolCheckOnFileName($src_file, $dest_file = '')
1414{
1415 if (preg_match('/\.pdf$/i', $dest_file)) {
1416 if (!getDolGlobalString('MAIN_ANTIVIRUS_ALLOW_JS_IN_PDF')) {
1417 dol_syslog("dolCheckOnFileName Check that pdf does not contains js code");
1418
1419 $tmp = file_get_contents(trim($src_file));
1420 if (preg_match('/[\n\s]+\/JavaScript[\n\s]+/m', $tmp)) {
1421 return array('File is a PDF with javascript inside');
1422 }
1423 } else {
1424 dol_syslog("dolCheckOnFileName Check js into pdf disabled");
1425 }
1426 }
1427
1428 return array();
1429}
1430
1431
1453function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan = 0, $uploaderrorcode = 0, $nohook = 0, $keyforsourcefile = 'addedfile', $upload_dir = '', $mode = 0)
1454{
1455 global $conf;
1456 global $object, $hookmanager;
1457
1458 $reshook = 0;
1459 $file_name = $dest_file;
1460 $successcode = 1;
1461
1462 if (empty($nohook)) {
1463 $reshook = $hookmanager->initHooks(array('fileslib'));
1464
1465 $parameters = array('dest_file' => $dest_file, 'src_file' => $src_file, 'file_name' => $file_name, 'varfiles' => $keyforsourcefile, 'allowoverwrite' => $allowoverwrite);
1466 $reshook = $hookmanager->executeHooks('moveUploadedFile', $parameters, $object);
1467 }
1468
1469 if (empty($reshook)) {
1470 // If an upload error has been reported
1471 if ($uploaderrorcode) {
1472 switch ($uploaderrorcode) {
1473 case UPLOAD_ERR_INI_SIZE: // 1
1474 return 'ErrorFileSizeTooLarge';
1475 case UPLOAD_ERR_FORM_SIZE: // 2
1476 return 'ErrorFileSizeTooLarge';
1477 case UPLOAD_ERR_PARTIAL: // 3
1478 return 'ErrorPartialFile';
1479 case UPLOAD_ERR_NO_TMP_DIR: //
1480 return 'ErrorNoTmpDir';
1481 case UPLOAD_ERR_CANT_WRITE:
1482 return 'ErrorFailedToWriteInDir';
1483 case UPLOAD_ERR_EXTENSION:
1484 return 'ErrorUploadBlockedByAddon';
1485 default:
1486 break;
1487 }
1488 }
1489
1490 // Security:
1491 // If we need to make a virus scan
1492 if (empty($disablevirusscan) && file_exists($src_file)) {
1493 $checkvirusarray = dolCheckVirus($src_file, $dest_file);
1494 if (count($checkvirusarray)) {
1495 dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$dest_file.'") KO with antivirus: errors='.implode(',', $checkvirusarray), LOG_WARNING);
1496 return 'ErrorFileIsInfectedWithAVirus: '.implode(',', $checkvirusarray);
1497 }
1498 }
1499
1500 // Security:
1501 // Disallow file with some extensions. We rename them.
1502 // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code.
1503 if (isAFileWithExecutableContent($dest_file) && !getDolGlobalString('MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED')) {
1504 // $upload_dir ends with a slash, so be must be sure the medias dir to compare to ends with slash too.
1505 $publicmediasdirwithslash = $conf->medias->multidir_output[$conf->entity];
1506 if (!preg_match('/\/$/', $publicmediasdirwithslash)) {
1507 $publicmediasdirwithslash .= '/';
1508 }
1509
1510 if (strpos($upload_dir, $publicmediasdirwithslash) !== 0 || !getDolGlobalInt("MAIN_DOCUMENT_DISABLE_NOEXE_IN_MEDIAS_DIR")) { // We never add .noexe on files into media directory
1511 $file_name .= '.noexe';
1512 $successcode = 2;
1513 }
1514 }
1515
1516 // Security:
1517 // We refuse cache files/dirs, upload using .. and pipes into filenames.
1518 if (preg_match('/^\./', basename($src_file)) || preg_match('/\.\./', $src_file) || preg_match('/[<>|]/', $src_file)) {
1519 dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING);
1520 return -1;
1521 }
1522
1523 // Security:
1524 // We refuse cache files/dirs, upload using .. and pipes into filenames.
1525 if (preg_match('/^\./', basename($dest_file)) || preg_match('/\.\./', $dest_file) || preg_match('/[<>|]/', $dest_file)) {
1526 dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING);
1527 return -2;
1528 }
1529 }
1530
1531 if ($reshook < 0) { // At least one blocking error returned by one hook
1532 $errmsg = implode(',', $hookmanager->errors);
1533 if (empty($errmsg)) {
1534 $errmsg = 'ErrorReturnedBySomeHooks'; // Should not occurs. Added if hook is bugged and does not set ->errors when there is error.
1535 }
1536 return $errmsg;
1537 } elseif (empty($reshook)) {
1538 // The file functions must be in OS filesystem encoding.
1539 $src_file_osencoded = dol_osencode($src_file);
1540 $file_name_osencoded = dol_osencode($file_name);
1541
1542 // Check if destination dir is writable
1543 if (!is_writable(dirname($file_name_osencoded))) {
1544 dol_syslog("Files.lib::dol_move_uploaded_file Dir ".dirname($file_name_osencoded)." is not writable. Return 'ErrorDirNotWritable'", LOG_WARNING);
1545 return 'ErrorDirNotWritable';
1546 }
1547
1548 // Check if destination file already exists
1549 if (!$allowoverwrite) {
1550 if (file_exists($file_name_osencoded)) {
1551 dol_syslog("Files.lib::dol_move_uploaded_file File ".$file_name." already exists. Return 'ErrorFileAlreadyExists'", LOG_WARNING);
1552 return 'ErrorFileAlreadyExists';
1553 }
1554 } else { // We are allowed to erase
1555 if (is_dir($file_name_osencoded)) { // If there is a directory with name of file to create
1556 dol_syslog("Files.lib::dol_move_uploaded_file A directory with name ".$file_name." already exists. Return 'ErrorDirWithFileNameAlreadyExists'", LOG_WARNING);
1557 return 'ErrorDirWithFileNameAlreadyExists';
1558 }
1559 }
1560
1561 // Move file using a simple system function
1562 if ($mode == 0) {
1563 $return = move_uploaded_file($src_file_osencoded, $file_name_osencoded);
1564 } else {
1565 $return = rename($src_file_osencoded, $file_name_osencoded);
1566 }
1567
1568 if ($return) {
1569 dolChmod($file_name_osencoded);
1570 dol_syslog("Files.lib::dol_move_uploaded_file Success to move ".$src_file." to ".$file_name." - Umask=" . getDolGlobalString('MAIN_UMASK'), LOG_DEBUG);
1571 return $successcode; // Success
1572 } else {
1573 dol_syslog("Files.lib::dol_move_uploaded_file Failed to move ".$src_file." to ".$file_name, LOG_ERR);
1574 return -3; // Unknown error
1575 }
1576 }
1577
1578 return $successcode; // Success
1579}
1580
1596function dol_delete_file($file, $disableglob = 0, $nophperrors = 0, $nohook = 0, $object = null, $allowdotdot = false, $indexdatabase = 1, $nolog = 0)
1597{
1598 global $db, $user;
1599 global $hookmanager;
1600
1601 if (empty($nolog)) {
1602 dol_syslog("dol_delete_file file=".$file." disableglob=".$disableglob." nophperrors=".$nophperrors." nohook=".$nohook);
1603 }
1604
1605 // Security:
1606 // We refuse transversal using .. and pipes into filenames.
1607 if ((!$allowdotdot && preg_match('/\.\./', $file)) || preg_match('/[<>|]/', $file)) {
1608 dol_syslog("Refused to delete file ".$file, LOG_WARNING);
1609 return false;
1610 }
1611
1612 $reshook = 0;
1613 if (empty($nohook) && !empty($hookmanager)) {
1614 $hookmanager->initHooks(array('fileslib'));
1615
1616 $parameters = array(
1617 'file' => $file,
1618 'disableglob' => $disableglob,
1619 'nophperrors' => $nophperrors
1620 );
1621 $reshook = $hookmanager->executeHooks('deleteFile', $parameters, $object);
1622 }
1623
1624 if (empty($nohook) && $reshook != 0) { // reshook = 0 to do standard actions, 1 = ok and replace, -1 = ko
1625 dol_syslog("reshook=".$reshook);
1626 if ($reshook < 0) {
1627 return false;
1628 }
1629 return true;
1630 } else {
1631 $file_osencoded = dol_osencode($file); // New filename encoded in OS filesystem encoding charset
1632 if (empty($disableglob) && !empty($file_osencoded)) {
1633 $ok = true;
1634 $globencoded = str_replace('[', '\[', $file_osencoded);
1635 $globencoded = str_replace(']', '\]', $globencoded);
1636 $listofdir = glob($globencoded); // This scan dir for files. If file does not exists, return empty.
1637
1638 if (!empty($listofdir) && is_array($listofdir)) {
1639 foreach ($listofdir as $filename) {
1640 if ($nophperrors) {
1641 $ok = @unlink($filename);
1642 } else {
1643 $ok = unlink($filename);
1644 }
1645
1646 // If it fails and it is because of the missing write permission on parent dir
1647 if (!$ok && file_exists(dirname($filename)) && !(fileperms(dirname($filename)) & 0200)) {
1648 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);
1649 dolChmod(dirname($filename), decoct(fileperms(dirname($filename)) | 0200));
1650 // Now we retry deletion
1651 if ($nophperrors) {
1652 $ok = @unlink($filename);
1653 } else {
1654 $ok = unlink($filename);
1655 }
1656 }
1657
1658 if ($ok) {
1659 if (empty($nolog)) {
1660 dol_syslog("Removed file ".$filename, LOG_DEBUG);
1661 }
1662
1663 // Delete entry into ecm database
1664 $rel_filetodelete = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filename);
1665 if (!preg_match('/(\/temp\/|\/thumbs\/|\.meta$)/', $rel_filetodelete)) { // If not a tmp file
1666 if (is_object($db) && $indexdatabase) { // $db may not be defined when lib is in a context with define('NOREQUIREDB',1)
1667 $rel_filetodelete = preg_replace('/^[\\/]/', '', $rel_filetodelete);
1668 $rel_filetodelete = preg_replace('/\.noexe$/', '', $rel_filetodelete);
1669
1670 dol_syslog("Try to remove also entries in database for full relative path = ".$rel_filetodelete, LOG_DEBUG);
1671 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1672 $ecmfile = new EcmFiles($db);
1673 $result = $ecmfile->fetch(0, '', $rel_filetodelete, '', '', '', 0, $object->entity ?? 0);
1674 if ($result >= 0 && $ecmfile->id > 0) {
1675 $result = $ecmfile->delete($user);
1676 }
1677 if ($result < 0) {
1678 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1679 }
1680 }
1681 }
1682 } else {
1683 dol_syslog("Failed to remove file ".$filename, LOG_WARNING);
1684 // TODO Failure to remove can be because file was already removed or because of permission
1685 // If error because it does not exists, we should return true, and we should return false if this is a permission problem
1686 }
1687 }
1688 } else {
1689 $ok = true; // nothing to delete when glob is on must return ok
1690 dol_syslog("No files to delete found", LOG_DEBUG);
1691 }
1692 } else {
1693 $ok = false;
1694 if ($nophperrors) {
1695 $ok = @unlink($file_osencoded);
1696 } else {
1697 $ok = unlink($file_osencoded);
1698 }
1699 if ($ok) {
1700 if (empty($nolog)) {
1701 dol_syslog("Removed file ".$file_osencoded, LOG_DEBUG);
1702 }
1703 } else {
1704 dol_syslog("Failed to remove file ".$file_osencoded, LOG_WARNING);
1705 }
1706 }
1707
1708 return $ok;
1709 }
1710}
1711
1721function dol_delete_dir($dir, $nophperrors = 0)
1722{
1723 // Security:
1724 // We refuse transversal using .. and pipes into filenames.
1725 if (preg_match('/\.\./', $dir) || preg_match('/[<>|]/', $dir)) {
1726 dol_syslog("Refused to delete dir ".$dir.' (contains invalid char sequence)', LOG_WARNING);
1727 return false;
1728 }
1729
1730 $dir_osencoded = dol_osencode($dir);
1731 return ($nophperrors ? @rmdir($dir_osencoded) : rmdir($dir_osencoded));
1732}
1733
1747function dol_delete_dir_recursive($dir, $count = 0, $nophperrors = 0, $onlysub = 0, &$countdeleted = 0, $indexdatabase = 1, $nolog = 0, $level = 0)
1748{
1749 if (empty($nolog) || empty($level)) {
1750 dol_syslog("functions.lib:dol_delete_dir_recursive ".$dir, LOG_DEBUG);
1751 }
1752 if ($level > 1000) {
1753 dol_syslog("functions.lib:dol_delete_dir_recursive too many depth", LOG_WARNING);
1754 }
1755
1756 if (dol_is_dir($dir)) {
1757 $dir_osencoded = dol_osencode($dir);
1758 if ($handle = opendir("$dir_osencoded")) {
1759 while (false !== ($item = readdir($handle))) {
1760 if (!utf8_check($item)) {
1761 $item = mb_convert_encoding($item, 'UTF-8', 'ISO-8859-1'); // should be useless
1762 }
1763
1764 if ($item != "." && $item != "..") {
1765 if (is_dir(dol_osencode("$dir/$item")) && !is_link(dol_osencode("$dir/$item"))) {
1766 $count = dol_delete_dir_recursive("$dir/$item", $count, $nophperrors, 0, $countdeleted, $indexdatabase, $nolog, ($level + 1));
1767 } else {
1768 $result = dol_delete_file("$dir/$item", 1, $nophperrors, 0, null, false, $indexdatabase, $nolog);
1769 $count++;
1770 if ($result) {
1771 $countdeleted++;
1772 }
1773 //else print 'Error on '.$item."\n";
1774 }
1775 }
1776 }
1777 closedir($handle);
1778
1779 // Delete also the main directory
1780 if (empty($onlysub)) {
1781 $result = dol_delete_dir($dir, $nophperrors);
1782 $count++;
1783 if ($result) {
1784 $countdeleted++;
1785 }
1786 //else print 'Error on '.$dir."\n";
1787 }
1788 }
1789 }
1790
1791 return $count;
1792}
1793
1794
1804{
1805 global $langs, $conf;
1806
1807 // Define parent dir of elements
1808 $element = $object->element;
1809
1810 if ($object->element == 'order_supplier') {
1811 $dir = $conf->fournisseur->commande->dir_output;
1812 } elseif ($object->element == 'invoice_supplier') {
1813 $dir = $conf->fournisseur->facture->dir_output;
1814 } elseif ($object->element == 'project') {
1815 $dir = $conf->project->dir_output;
1816 } elseif ($object->element == 'shipping') {
1817 $dir = $conf->expedition->dir_output.'/sending';
1818 } elseif ($object->element == 'delivery') {
1819 $dir = $conf->expedition->dir_output.'/receipt';
1820 } elseif ($object->element == 'fichinter') {
1821 $dir = $conf->ficheinter->dir_output;
1822 } else {
1823 $dir = empty($conf->$element->dir_output) ? '' : $conf->$element->dir_output;
1824 }
1825
1826 if (empty($dir)) {
1827 $object->error = $langs->trans('ErrorObjectNoSupportedByFunction');
1828 return 0;
1829 }
1830
1831 $refsan = dol_sanitizeFileName($object->ref);
1832 $dir = $dir."/".$refsan;
1833 $filepreviewnew = $dir."/".$refsan.".pdf_preview.png";
1834 $filepreviewnewbis = $dir."/".$refsan.".pdf_preview-0.png";
1835 $filepreviewold = $dir."/".$refsan.".pdf.png";
1836
1837 // For new preview files
1838 if (file_exists($filepreviewnew) && is_writable($filepreviewnew)) {
1839 if (!dol_delete_file($filepreviewnew, 1)) {
1840 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewnew);
1841 return 0;
1842 }
1843 }
1844 if (file_exists($filepreviewnewbis) && is_writable($filepreviewnewbis)) {
1845 if (!dol_delete_file($filepreviewnewbis, 1)) {
1846 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewnewbis);
1847 return 0;
1848 }
1849 }
1850 // For old preview files
1851 if (file_exists($filepreviewold) && is_writable($filepreviewold)) {
1852 if (!dol_delete_file($filepreviewold, 1)) {
1853 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewold);
1854 return 0;
1855 }
1856 } else {
1857 $multiple = $filepreviewold.".";
1858 for ($i = 0; $i < 20; $i++) {
1859 $preview = $multiple.$i;
1860
1861 if (file_exists($preview) && is_writable($preview)) {
1862 if (!dol_delete_file($preview, 1)) {
1863 $object->error = $langs->trans("ErrorFailedToOpenFile", $preview);
1864 return 0;
1865 }
1866 }
1867 }
1868 }
1869
1870 return 1;
1871}
1872
1882{
1883 global $conf;
1884
1885 // Create meta file
1886 if (!getDolGlobalString('MAIN_DOC_CREATE_METAFILE')) {
1887 return 0; // By default, no metafile.
1888 }
1889
1890 // Define parent dir of elements
1891 $element = $object->element;
1892
1893 if ($object->element == 'order_supplier') {
1894 $dir = $conf->fournisseur->dir_output.'/commande';
1895 } elseif ($object->element == 'invoice_supplier') {
1896 $dir = $conf->fournisseur->dir_output.'/facture';
1897 } elseif ($object->element == 'project') {
1898 $dir = $conf->project->dir_output;
1899 } elseif ($object->element == 'shipping') {
1900 $dir = $conf->expedition->dir_output.'/sending';
1901 } elseif ($object->element == 'delivery') {
1902 $dir = $conf->expedition->dir_output.'/receipt';
1903 } elseif ($object->element == 'fichinter') {
1904 $dir = $conf->ficheinter->dir_output;
1905 } else {
1906 $dir = empty($conf->$element->dir_output) ? '' : $conf->$element->dir_output;
1907 }
1908
1909 if ($dir) {
1910 $object->fetch_thirdparty();
1911
1912 $objectref = dol_sanitizeFileName($object->ref);
1913 $dir = $dir."/".$objectref;
1914 $file = $dir."/".$objectref.".meta";
1915
1916 if (!is_dir($dir)) {
1917 dol_mkdir($dir);
1918 }
1919
1920 $meta = '';
1921 if (is_dir($dir)) {
1922 if (is_countable($object->lines) && count($object->lines) > 0) {
1923 $nblines = count($object->lines);
1924 } else {
1925 $nblines = 0;
1926 }
1927 $client = $object->thirdparty->name." ".$object->thirdparty->address." ".$object->thirdparty->zip." ".$object->thirdparty->town;
1928 $meta = "REFERENCE=\"".$object->ref."\"
1929 DATE=\"" . dol_print_date($object->date, '')."\"
1930 NB_ITEMS=\"" . $nblines."\"
1931 CLIENT=\"" . $client."\"
1932 AMOUNT_EXCL_TAX=\"" . $object->total_ht."\"
1933 AMOUNT=\"" . $object->total_ttc."\"\n";
1934
1935 for ($i = 0; $i < $nblines; $i++) {
1936 //Pour les articles
1937 $meta .= "ITEM_".$i."_QUANTITY=\"".$object->lines[$i]->qty."\"
1938 ITEM_" . $i."_AMOUNT_WO_TAX=\"".$object->lines[$i]->total_ht."\"
1939 ITEM_" . $i."_VAT=\"".$object->lines[$i]->tva_tx."\"
1940 ITEM_" . $i."_DESCRIPTION=\"".str_replace("\r\n", "", nl2br($object->lines[$i]->desc))."\"
1941 ";
1942 }
1943 }
1944
1945 $fp = fopen($file, "w");
1946 fwrite($fp, $meta);
1947 fclose($fp);
1948
1949 dolChmod($file);
1950
1951 return 1;
1952 } else {
1953 dol_syslog('FailedToDetectDirInDolMetaCreateFor'.$object->element, LOG_WARNING);
1954 }
1955
1956 return 0;
1957}
1958
1959
1960
1969function dol_init_file_process($pathtoscan = '', $trackid = '')
1970{
1971 $listofpaths = array();
1972 $listofnames = array();
1973 $listofmimes = array();
1974
1975 if ($pathtoscan) {
1976 $listoffiles = dol_dir_list($pathtoscan, 'files');
1977 foreach ($listoffiles as $key => $val) {
1978 $listofpaths[] = $val['fullname'];
1979 $listofnames[] = $val['name'];
1980 $listofmimes[] = dol_mimetype($val['name']);
1981 }
1982 }
1983 $keytoavoidconflict = empty($trackid) ? '' : '-'.$trackid;
1984 $_SESSION["listofpaths".$keytoavoidconflict] = implode(';', $listofpaths);
1985 $_SESSION["listofnames".$keytoavoidconflict] = implode(';', $listofnames);
1986 $_SESSION["listofmimes".$keytoavoidconflict] = implode(';', $listofmimes);
1987}
1988
1989
2010function dol_add_file_process($upload_dir, $allowoverwrite = 0, $updatesessionordb = 0, $keyforsourcefile = 'addedfile', $savingdocmask = '', $link = null, $trackid = '', $generatethumbs = 1, $object = null, $forceFullTextIndexation = '', $mode = 0)
2011{
2012 global $db, $user, $conf, $langs;
2013
2014 $res = 0;
2015
2016 // If mode 1, prepare environment to be compatible with mode 0
2017 if ($mode == 1) {
2018 $_FILES = array($keyforsourcefile => array());
2019 $_FILES[$keyforsourcefile]['tmp_name'] = $keyforsourcefile;
2020 $_FILES[$keyforsourcefile]['name'] = $keyforsourcefile;
2021 $mode = 0;
2022 }
2023
2024 if (!empty($_FILES[$keyforsourcefile])) { // For view $_FILES[$keyforsourcefile]['error']
2025 dol_syslog('dol_add_file_process varfiles = '.$keyforsourcefile.' upload_dir='.$upload_dir.' allowoverwrite='.$allowoverwrite.' updatesessionordb='.$updatesessionordb.' savingdocmask='.$savingdocmask, LOG_DEBUG);
2026 $maxfilesinform = getDolGlobalInt("MAIN_SECURITY_MAX_ATTACHMENT_ON_FORMS", 10);
2027 if (is_array($_FILES[$keyforsourcefile]["name"]) && count($_FILES[$keyforsourcefile]["name"]) > $maxfilesinform) {
2028 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2029 setEventMessages($langs->trans("ErrorTooMuchFileInForm", $maxfilesinform), null, "errors");
2030 return -1;
2031 }
2032
2033 $result = dol_mkdir($upload_dir);
2034 //var_dump($result);exit;
2035
2036 if ($result >= 0) {
2037 $TFile = $_FILES[$keyforsourcefile];
2038 // Convert value of $TFile
2039 if (!is_array($TFile['name'])) {
2040 foreach ($TFile as $key => &$val) {
2041 $val = array($val);
2042 }
2043 }
2044
2045 $nbfile = count($TFile['name']);
2046 $nbok = 0;
2047 for ($i = 0; $i < $nbfile; $i++) {
2048 if (empty($TFile['name'][$i])) {
2049 continue; // For example, when submitting a form with no file name
2050 }
2051
2052 // Define $destfull (path to file including filename) and $destfile (only filename)
2053 $destfile = trim($TFile['name'][$i]);
2054 $destfull = $upload_dir."/".$destfile;
2055 $destfilewithoutext = preg_replace('/\.[^\.]+$/', '', $destfile);
2056
2057 if ($savingdocmask && strpos($savingdocmask, $destfilewithoutext) !== 0) {
2058 $destfile = trim(preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask));
2059 $destfull = $upload_dir."/".$destfile;
2060 }
2061
2062 $filenameto = basename($destfile);
2063 if (preg_match('/^\./', $filenameto)) {
2064 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2065 setEventMessages($langs->trans("ErrorFilenameCantStartWithDot", $filenameto), null, 'errors');
2066 break;
2067 }
2068 // dol_sanitizeFileName the file name and lowercase extension
2069 $info = pathinfo($destfull);
2070 $destfull = $info['dirname'].'/'.dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : ''));
2071 $info = pathinfo($destfile);
2072 $destfile = dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : ''));
2073
2074 // Check extension is allowed for upload
2075 $fileextensionrestriction = getDolGlobalString("MAIN_FILE_EXTENSION_UPLOAD_RESTRICTION", implode(',', getExecutableContent()));
2076 if (!empty($fileextensionrestriction)) {
2077 $arrayofregexextension = explode(",", $fileextensionrestriction);
2078
2079 foreach ($arrayofregexextension as $fileextension) {
2080 if (preg_match('/\.'.preg_quote(trim($fileextension), '/').'$/i', $destfull)) {
2081 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2082 setEventMessages($langs->trans("ErrorFilenameExtensionNotAllowed", $filenameto), null, 'errors');
2083 return -1;
2084 }
2085 }
2086 }
2087
2088 // We apply dol_string_nohtmltag also to clean file names (this remove duplicate spaces) because
2089 // this function is also applied when we rename and when we make try to download file (by the GETPOST(filename, 'alphanohtml') call).
2090 $destfile = dol_string_nohtmltag($destfile);
2091 $destfull = dol_string_nohtmltag($destfull);
2092
2093 // Check that filename is not the one of a reserved allowed CLI command
2094 global $dolibarr_main_restrict_os_commands;
2095 if (!empty($dolibarr_main_restrict_os_commands)) {
2096 $arrayofallowedcommand = explode(',', $dolibarr_main_restrict_os_commands);
2097 $arrayofallowedcommand = array_map('trim', $arrayofallowedcommand);
2098 if (in_array($destfile, $arrayofallowedcommand)) {
2099 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2100 setEventMessages($langs->trans("ErrorFilenameReserved", $destfile), null, 'errors');
2101 return -1;
2102 }
2103 }
2104
2105 // Move file from source directory to final destination. Check for virus is also embedded and a .noexe may also be appended on file name.
2106 $resupload = dol_move_uploaded_file($TFile['tmp_name'][$i], $destfull, $allowoverwrite, 0, $TFile['error'][$i], 0, $keyforsourcefile, $upload_dir, $mode);
2107
2108 if (is_numeric($resupload) && $resupload > 0) { // $resupload can be 'ErrorFileAlreadyExists', 'ErrorFileIsInfectedWithAVirus...'
2109 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2110
2111 $tmparraysize = getDefaultImageSizes();
2112 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
2113 $maxheightsmall = $tmparraysize['maxheightsmall'];
2114 $maxwidthmini = $tmparraysize['maxwidthmini'];
2115 $maxheightmini = $tmparraysize['maxheightmini'];
2116 //$quality = $tmparraysize['quality'];
2117 $quality = 50; // For thumbs, we force quality to 50
2118
2119 // Generate thumbs.
2120 if ($generatethumbs) {
2121 if (image_format_supported($destfull) == 1) {
2122 // Create thumbs
2123 // We can't use $object->addThumbs here because there is no $object known
2124
2125 // Used on logon for example
2126 $imgThumbSmall = vignette($destfull, $maxwidthsmall, $maxheightsmall, '_small', $quality, "thumbs");
2127 // Create mini thumbs for image (Ratio is near 16/9)
2128 // Used on menu or for setup page for example
2129 $imgThumbMini = vignette($destfull, $maxwidthmini, $maxheightmini, '_mini', $quality, "thumbs");
2130 }
2131 }
2132
2133 // Update session
2134 if (empty($updatesessionordb)) {
2135 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2136 $formmail = new FormMail($db);
2137 $formmail->trackid = $trackid;
2138 $formmail->add_attached_files($destfull, $destfile, $TFile['type'][$i]);
2139 }
2140
2141 // Update index table of files (llx_ecm_files)
2142 if ($updatesessionordb == 1) {
2143 $sharefile = 0;
2144 if ($TFile['type'][$i] == 'application/pdf' && strpos($_SERVER["REQUEST_URI"], 'product') !== false && getDolGlobalString('PRODUCT_ALLOW_EXTERNAL_DOWNLOAD')) {
2145 $sharefile = 1;
2146 }
2147
2148 // If we allow overwrite, we may need to also overwrite index, so we delete index first so insert can work
2149 if ($allowoverwrite) {
2150 deleteFilesIntoDatabaseIndex($upload_dir, basename($destfile).($resupload == 2 ? '.noexe' : ''), '', $object);
2151 }
2152
2153 $result = addFileIntoDatabaseIndex($upload_dir, basename($destfile).($resupload == 2 ? '.noexe' : ''), $TFile['name'][$i], 'uploaded', $sharefile, $object, $forceFullTextIndexation);
2154 if ($result < 0) {
2155 if ($allowoverwrite) {
2156 // Do not show error message. We can have an error due to DB_ERROR_RECORD_ALREADY_EXISTS
2157 } else {
2158 setEventMessages('WarningFailedToAddFileIntoDatabaseIndex', null, 'warnings');
2159 }
2160 }
2161 }
2162
2163 $nbok++;
2164 } else {
2165 $langs->load("errors");
2166 if (is_numeric($resupload) && $resupload < 0) { // Unknown error
2167 setEventMessages($langs->trans("ErrorFileNotUploaded"), null, 'errors');
2168 } elseif (preg_match('/ErrorFileIsInfectedWithAVirus/', $resupload)) { // Files infected by a virus
2169 if (preg_match('/File is a PDF with javascript inside/', $resupload)) {
2170 setEventMessages($langs->trans("ErrorFileIsAnInfectedPDFWithJSInside"), null, 'errors');
2171 } else {
2172 setEventMessages($langs->trans("ErrorFileIsInfectedWithAVirus").'<br>'.dolGetFirstLineOfText($resupload), null, 'errors');
2173 }
2174 } else { // Known error
2175 setEventMessages($langs->trans($resupload), null, 'errors');
2176 }
2177 }
2178 }
2179 if ($nbok > 0) {
2180 $res = $nbok;
2181 setEventMessages($langs->trans("FileTransferComplete"), null, 'mesgs');
2182 }
2183 } else {
2184 setEventMessages($langs->trans("ErrorFailedToCreateDir", $upload_dir), null, 'errors');
2185 }
2186 } elseif ($link) {
2187 require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php';
2188 $linkObject = new Link($db);
2189 $linkObject->entity = $conf->entity;
2190 $linkObject->url = $link;
2191 $linkObject->objecttype = GETPOST('objecttype', 'alpha');
2192 $linkObject->objectid = GETPOSTINT('objectid');
2193 $linkObject->label = GETPOST('label', 'alpha');
2194 $res = $linkObject->create($user);
2195
2196 if ($res > 0) {
2197 setEventMessages($langs->trans("LinkComplete"), null, 'mesgs');
2198 } else {
2199 setEventMessages($langs->trans("ErrorFileNotLinked"), null, 'errors');
2200 }
2201 } else {
2202 $langs->load("errors");
2203 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("File")), null, 'errors');
2204 }
2205
2206 return $res;
2207}
2208
2209
2221function dol_remove_file_process($filenb, $donotupdatesession = 0, $donotdeletefile = 1, $trackid = '')
2222{
2223 global $db, $user, $conf, $langs, $_FILES;
2224
2225 $keytodelete = $filenb;
2226 $keytodelete--;
2227
2228 $listofpaths = array();
2229 $listofnames = array();
2230 $listofmimes = array();
2231 $keytoavoidconflict = empty($trackid) ? '' : '-'.$trackid;
2232 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
2233 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
2234 }
2235 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
2236 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
2237 }
2238 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
2239 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
2240 }
2241
2242 if ($keytodelete >= 0) {
2243 $pathtodelete = $listofpaths[$keytodelete];
2244 $filetodelete = $listofnames[$keytodelete];
2245 if (empty($donotdeletefile)) {
2246 $result = dol_delete_file($pathtodelete, 1); // The delete of ecm database is inside the function dol_delete_file
2247 } else {
2248 $result = 0;
2249 }
2250 if ($result >= 0) {
2251 if (empty($donotdeletefile)) {
2252 $langs->load("other");
2253 setEventMessages($langs->trans("FileWasRemoved", $filetodelete), null, 'mesgs');
2254 }
2255 if (empty($donotupdatesession)) {
2256 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2257 $formmail = new FormMail($db);
2258 $formmail->trackid = $trackid;
2259 $formmail->remove_attached_files($keytodelete);
2260 }
2261 }
2262 }
2263}
2264
2265
2280function addFileIntoDatabaseIndex($dir, $file, $fullpathorig = '', $mode = 'uploaded', $setsharekey = 0, $object = null, $forceFullTextIndexation = '')
2281{
2282 global $db, $user, $conf;
2283
2284 $result = 0;
2285 $error = 0;
2286
2287 dol_syslog("addFileIntoDatabaseIndex dir=".$dir." file=".$file, LOG_DEBUG);
2288
2289 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
2290
2291 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a temporary directory. TODO Does this test work ?
2292 $filename = basename(preg_replace('/\.noexe$/', '', $file));
2293 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
2294 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
2295
2296 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
2297 $ecmfile = new EcmFiles($db);
2298 $ecmfile->filepath = $rel_dir;
2299 $ecmfile->filename = $filename;
2300 $ecmfile->label = md5_file(dol_osencode($dir.'/'.$file)); // MD5 of file content
2301 $ecmfile->fullpath_orig = $fullpathorig;
2302 $ecmfile->gen_or_uploaded = $mode;
2303 $ecmfile->description = ''; // indexed content
2304 $ecmfile->keywords = ''; // keyword content
2305
2306 if (is_object($object) && $object->id > 0) {
2307 $ecmfile->src_object_id = $object->id;
2308 if (isset($object->table_element)) {
2309 $ecmfile->src_object_type = $object->table_element;
2310 } else {
2311 dol_syslog('Error: object ' . get_class($object) . ' has no table_element attribute.');
2312 return -1;
2313 }
2314 if (isset($object->src_object_description)) {
2315 $ecmfile->description = $object->src_object_description;
2316 }
2317 if (isset($object->src_object_keywords)) {
2318 $ecmfile->keywords = $object->src_object_keywords;
2319 }
2320 if (isset($object->entity)) {
2321 $ecmfile->entity = $object->entity;
2322 }
2323 }
2324
2325 if (getDolGlobalString('MAIN_FORCE_SHARING_ON_ANY_UPLOADED_FILE')) {
2326 $setsharekey = 1;
2327 }
2328
2329 if ($setsharekey) {
2330 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
2331 $ecmfile->share = getRandomPassword(true);
2332 }
2333
2334 // Use a convertisser Doc to Text
2335 $useFullTextIndexation = getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT');
2336 if (empty($useFullTextIndexation) && $forceFullTextIndexation == '1') {
2337 if (getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_PDFTOTEXT')) {
2338 $useFullTextIndexation = 'pdftotext';
2339 } elseif (getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_DOCLING')) {
2340 $useFullTextIndexation = 'docling';
2341 }
2342 }
2343
2344 //$useFullTextIndexation = 1;
2345 if ($useFullTextIndexation) {
2346 $ecmfile->filepath = $rel_dir;
2347 $ecmfile->filename = $filename;
2348
2349 $filetoprocess = $dir.'/'.$ecmfile->filename;
2350
2351 $textforfulltextindex = '';
2352 $keywords = '';
2353 $cmd = '';
2354 if (preg_match('/\.pdf/i', $filename)) {
2355 // TODO Move this into external submodule files
2356
2357 // TODO Develop a native PHP parser using sample code in https://github.com/adeel/php-pdf-parser
2358
2359 // Use the method pdftotext to generate a HTML
2360 if (preg_match('/pdftotext/i', $useFullTextIndexation)) {
2361 include_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
2362 $utils = new Utils($db);
2363 $outputfile = $conf->admin->dir_temp.'/tmppdftotext.'.$user->id.'.out'; // File used with popen method
2364
2365 // We also exclude '/temp/' dir and 'documents/admin/documents'
2366 // We make escapement here and call executeCLI without escapement because we don't want to have the '*.log' escaped.
2367 $cmd = getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_PDFTOTEXT', 'pdftotext')." -htmlmeta '".escapeshellcmd($filetoprocess)."' - ";
2368 $resultexec = $utils->executeCLI($cmd, $outputfile, 0, null, 1);
2369
2370 if (!$resultexec['error']) {
2371 $txt = $resultexec['output'];
2372 $matches = array();
2373 if (preg_match('/<meta name="Keywords" content="([^\/]+)"\s*\/>/i', $txt, $matches)) {
2374 $keywords = $matches[1];
2375 }
2376 if (preg_match('/<pre>(.*)<\/pre>/si', $txt, $matches)) {
2377 $textforfulltextindex = dol_string_nounprintableascii($matches[1], 0);
2378 }
2379 } else {
2380 dol_syslog($resultexec['error']);
2381 $error++;
2382 }
2383 }
2384
2385 // Use the method docling to generate a .md (https://ds4sd.github.io/docling/)
2386 if (preg_match('/docling/i', $useFullTextIndexation)) {
2387 include_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
2388 $utils = new Utils($db);
2389 $outputfile = $conf->admin->dir_temp.'/tmpdocling.'.$user->id.'.out'; // File used with popen method
2390
2391 // We also exclude '/temp/' dir and 'documents/admin/documents'
2392 // We make escapement here and call executeCLI without escapement because we don't want to have the '*.log' escaped.
2393 $cmd = getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_DOCLING', 'docling')." --from pdf --to text '".escapeshellcmd($filetoprocess)."'";
2394 $resultexec = $utils->executeCLI($cmd, $outputfile, 0, null, 1);
2395
2396 if (!$resultexec['error']) {
2397 $txt = $resultexec['output'];
2398 //$matches = array();
2399 //if (preg_match('/<meta name="Keywords" content="([^\/]+)"\s*\/>/i', $txt, $matches)) {
2400 // $keywords = $matches[1];
2401 //}
2402 //if (preg_match('/<pre>(.*)<\/pre>/si', $txt, $matches)) {
2403 // $textforfulltextindex = dol_string_nounprintableascii($matches[1], 0);
2404 //}
2405 $textforfulltextindex = $txt;
2406 } else {
2407 dol_syslog($resultexec['error']);
2408 $error++;
2409 }
2410 }
2411 }
2412
2413 if ($cmd) {
2414 $ecmfile->description = 'File content generated by '.$cmd;
2415 }
2416 $ecmfile->content = $textforfulltextindex;
2417 $ecmfile->keywords = $keywords;
2418 }
2419
2420 if (!$error) {
2421 $result = $ecmfile->create($user);
2422 if ($result < 0) {
2423 dol_syslog($ecmfile->error);
2424 }
2425 }
2426 }
2427
2428 return $result;
2429}
2430
2440function deleteFilesIntoDatabaseIndex($dir, $file, $mode = 'uploaded', $object = null)
2441{
2442 global $conf, $db;
2443
2444 $error = 0;
2445
2446 if (empty($dir)) {
2447 dol_syslog("deleteFilesIntoDatabaseIndex: dir parameter can't be empty", LOG_ERR);
2448 return -1;
2449 }
2450
2451 dol_syslog("deleteFilesIntoDatabaseIndex dir=".$dir." file=".$file, LOG_DEBUG);
2452
2453 $db->begin();
2454
2455 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
2456
2457 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a temporary directory. TODO Does this test work ?
2458 $filename = basename($file);
2459 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
2460 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
2461
2462 if (!$error) {
2463 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'ecm_files';
2464 if (isset($object->entity)) {
2465 $sql .= ' WHERE entity = ' . ((int) $object->entity);
2466 } else {
2467 $sql .= ' WHERE entity = ' . ((int) $conf->entity);
2468 }
2469 $sql .= " AND filepath = '".$db->escape($rel_dir)."'";
2470 if ($file) {
2471 $sql .= " AND filename = '".$db->escape($file)."'";
2472 }
2473 if ($mode) {
2474 $sql .= " AND gen_or_uploaded = '".$db->escape($mode)."'";
2475 }
2476
2477 $resql = $db->query($sql);
2478 if (!$resql) {
2479 $error++;
2480 dol_syslog(__FUNCTION__.' '.$db->lasterror(), LOG_ERR);
2481 }
2482 }
2483 }
2484
2485 // Commit or rollback
2486 if ($error) {
2487 $db->rollback();
2488 return -1 * $error;
2489 } else {
2490 $db->commit();
2491 return 1;
2492 }
2493}
2494
2495
2507function dol_convert_file($fileinput, $ext = 'png', $fileoutput = '', $page = '')
2508{
2509 if (class_exists('Imagick')) {
2510 $image = new Imagick();
2511 try {
2512 $filetoconvert = $fileinput.(($page != '') ? '['.$page.']' : '');
2513 //var_dump($filetoconvert);
2514 $ret = $image->readImage($filetoconvert);
2515 } catch (Exception $e) {
2516 $ext = pathinfo($fileinput, PATHINFO_EXTENSION);
2517 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);
2518 return 0;
2519 }
2520 if ($ret) {
2521 $ret = $image->setImageFormat($ext);
2522 if ($ret) {
2523 if (empty($fileoutput)) {
2524 $fileoutput = $fileinput.".".$ext;
2525 }
2526
2527 $count = $image->getNumberImages();
2528
2529 if (!dol_is_file($fileoutput) || is_writable($fileoutput)) {
2530 try {
2531 $ret = $image->writeImages($fileoutput, true);
2532 } catch (Exception $e) {
2533 dol_syslog($e->getMessage(), LOG_WARNING);
2534 }
2535 } else {
2536 dol_syslog("Warning: Failed to write cache preview file '.$fileoutput.'. Check permission on file/dir", LOG_ERR);
2537 }
2538 if ($ret) {
2539 return $count;
2540 } else {
2541 return -3;
2542 }
2543 } else {
2544 return -2;
2545 }
2546 } else {
2547 return -1;
2548 }
2549 } else {
2550 return 0;
2551 }
2552}
2553
2554
2566function dol_compress_file($inputfile, $outputfile, $mode = "gz", &$errorstring = null)
2567{
2568 $foundhandler = 0;
2569 //var_dump(basename($inputfile)); exit;
2570
2571 try {
2572 dol_syslog("dol_compress_file mode=".$mode." inputfile=".$inputfile." outputfile=".$outputfile);
2573
2574 $data = implode("", file(dol_osencode($inputfile)));
2575 $compressdata = null;
2576 if ($mode == 'gz' && function_exists('gzencode')) {
2577 $foundhandler = 1;
2578 $compressdata = gzencode($data, 9);
2579 } elseif ($mode == 'bz' && function_exists('bzcompress')) {
2580 $foundhandler = 1;
2581 $compressdata = bzcompress($data, 9);
2582 } elseif ($mode == 'zstd' && function_exists('zstd_compress')) {
2583 $foundhandler = 1;
2584 $compressdata = zstd_compress($data, 9);
2585 } elseif ($mode == 'zip') {
2586 if (class_exists('ZipArchive') && getDolGlobalString('MAIN_USE_ZIPARCHIVE_FOR_ZIP_COMPRESS')) {
2587 $foundhandler = 1;
2588
2589 $rootPath = realpath($inputfile);
2590
2591 dol_syslog("Class ZipArchive is set so we zip using ZipArchive to zip into ".$outputfile.' rootPath='.$rootPath);
2592 $zip = new ZipArchive();
2593
2594 if ($zip->open($outputfile, ZipArchive::CREATE) !== true) {
2595 $errorstring = "dol_compress_file failure - Failed to open file ".$outputfile."\n";
2596 dol_syslog($errorstring, LOG_ERR);
2597
2598 global $errormsg;
2599 $errormsg = $errorstring;
2600
2601 return -6;
2602 }
2603
2604 // Create recursive directory iterator
2606 $files = new RecursiveIteratorIterator(
2607 new RecursiveDirectoryIterator($rootPath, FilesystemIterator::UNIX_PATHS),
2608 RecursiveIteratorIterator::LEAVES_ONLY
2609 );
2610 '@phan-var-force SplFileInfo[] $files';
2611
2612 foreach ($files as $name => $file) {
2613 // Skip directories (they would be added automatically)
2614 if (!$file->isDir()) {
2615 // Get real and relative path for current file
2616 $filePath = $file->getPath(); // the full path with filename using the $inputdir root.
2617 $fileName = $file->getFilename();
2618 $fileFullRealPath = $file->getRealPath(); // the full path with name and transformed to use real path directory.
2619
2620 //$relativePath = substr($fileFullRealPath, strlen($rootPath) + 1);
2621 $relativePath = substr(($filePath ? $filePath.'/' : '').$fileName, strlen($rootPath) + 1);
2622
2623 // Add current file to archive
2624 $zip->addFile($fileFullRealPath, $relativePath);
2625 }
2626 }
2627
2628 // Zip archive will be created only after closing object
2629 $zip->close();
2630
2631 dol_syslog("dol_compress_file success - ".$zip->numFiles." files");
2632 return 1;
2633 }
2634
2635 if (defined('ODTPHP_PATHTOPCLZIP')) {
2636 $foundhandler = 1;
2637
2638 include_once ODTPHP_PATHTOPCLZIP.'pclzip.lib.php';
2639 $archive = new PclZip($outputfile);
2640
2641 $result = $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
2642
2643 if ($result === 0) {
2644 global $errormsg;
2645 $errormsg = $archive->errorInfo(true);
2646
2647 if ($archive->errorCode() == PCLZIP_ERR_WRITE_OPEN_FAIL) {
2648 $errorstring = "PCLZIP_ERR_WRITE_OPEN_FAIL";
2649 dol_syslog("dol_compress_file error - archive->errorCode() = PCLZIP_ERR_WRITE_OPEN_FAIL", LOG_ERR);
2650 return -4;
2651 }
2652
2653 $errorstring = "dol_compress_file error archive->errorCode = ".$archive->errorCode()." errormsg=".$errormsg;
2654 dol_syslog("dol_compress_file failure - ".$errormsg, LOG_ERR);
2655 return -3;
2656 } else {
2657 dol_syslog("dol_compress_file success - ".count($result)." files");
2658 return 1;
2659 }
2660 }
2661 }
2662
2663 if ($foundhandler && is_string($compressdata)) {
2664 $fp = fopen($outputfile, "w");
2665 fwrite($fp, $compressdata);
2666 fclose($fp);
2667 return 1;
2668 } else {
2669 $errorstring = "Try to zip with format ".$mode." with no handler for this format";
2670 dol_syslog($errorstring, LOG_ERR);
2671
2672 global $errormsg;
2673 $errormsg = $errorstring;
2674 return -2;
2675 }
2676 } catch (Exception $e) {
2677 global $langs, $errormsg;
2678 $langs->load("errors");
2679 $errormsg = $langs->trans("ErrorFailedToWriteInDir");
2680
2681 $errorstring = "Failed to open file ".$outputfile;
2682 dol_syslog($errorstring, LOG_ERR);
2683 return -1;
2684 }
2685}
2686
2695function dol_uncompress($inputfile, $outputdir)
2696{
2697 global $langs, $db;
2698
2699 $fileinfo = pathinfo($inputfile);
2700 $fileinfo["extension"] = strtolower($fileinfo["extension"]);
2701
2702 if ($fileinfo["extension"] == "zip") {
2703 if (defined('ODTPHP_PATHTOPCLZIP') && !getDolGlobalString('MAIN_USE_ZIPARCHIVE_FOR_ZIP_UNCOMPRESS')) {
2704 dol_syslog("Constant ODTPHP_PATHTOPCLZIP for pclzip library is set to ".ODTPHP_PATHTOPCLZIP.", so we use Pclzip to unzip into ".$outputdir);
2705 include_once ODTPHP_PATHTOPCLZIP.'pclzip.lib.php';
2706 $archive = new PclZip($inputfile);
2707
2708 // We create output dir manually, so it uses the correct permission (When created by the archive->extract, dir is rwx for everybody).
2709 dol_mkdir(dol_sanitizePathName($outputdir));
2710
2711 try {
2712 // Extract into outputdir, but only files that match the regex '/^((?!\.\.).)*$/' that means "does not include .."
2713 $result = $archive->extract(PCLZIP_OPT_PATH, $outputdir, PCLZIP_OPT_BY_PREG, '/^((?!\.\.).)*$/');
2714 } catch (Exception $e) {
2715 return array('error' => $e->getMessage());
2716 }
2717
2718 if (!is_array($result) && $result <= 0) {
2719 return array('error' => $archive->errorInfo(true));
2720 } else {
2721 $ok = 1;
2722 $errmsg = '';
2723 // Loop on each file to check result for unzipping file
2724 foreach ($result as $key => $val) {
2725 if ($val['status'] == 'path_creation_fail') {
2726 $langs->load("errors");
2727 $ok = 0;
2728 $errmsg = $langs->trans("ErrorFailToCreateDir", $val['filename']);
2729 break;
2730 }
2731 if ($val['status'] == 'write_protected') {
2732 $langs->load("errors");
2733 $ok = 0;
2734 $errmsg = $langs->trans("ErrorFailToCreateFile", $val['filename']);
2735 break;
2736 }
2737 }
2738
2739 if ($ok) {
2740 return array();
2741 } else {
2742 return array('error' => $errmsg);
2743 }
2744 }
2745 }
2746
2747 if (class_exists('ZipArchive')) { // Must install php-zip to have it
2748 dol_syslog("Class ZipArchive is set so we unzip using ZipArchive to unzip into ".$outputdir);
2749 $zip = new ZipArchive();
2750 $res = $zip->open($inputfile);
2751 if ($res === true) {
2752 //$zip->extractTo($outputdir.'/');
2753 // 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
2754 // python3 path_traversal_archiver.py <Created_file_name> test.zip -l 10 -p tmp/
2755 // with -l is the range of dot to go back in path.
2756 // and path_traversal_archiver.py found at https://github.com/Alamot/code-snippets/blob/master/path_traversal/path_traversal_archiver.py
2757 for ($i = 0; $i < $zip->numFiles; $i++) {
2758 if (preg_match('/\.\./', $zip->getNameIndex($i))) {
2759 dol_syslog("Warning: Try to unzip a file with a transversal path ".$zip->getNameIndex($i), LOG_WARNING);
2760 continue; // Discard the file
2761 }
2762 $zip->extractTo($outputdir.'/', array($zip->getNameIndex($i)));
2763 }
2764
2765 $zip->close();
2766 return array();
2767 } else {
2768 return array('error' => 'ErrUnzipFails');
2769 }
2770 }
2771
2772 return array('error' => 'ErrNoZipEngine');
2773 } elseif (in_array($fileinfo["extension"], array('gz', 'bz2', 'zst'))) {
2774 include_once DOL_DOCUMENT_ROOT."/core/class/utils.class.php";
2775 $utils = new Utils($db);
2776
2777 dol_mkdir(dol_sanitizePathName($outputdir));
2778 $outputfilename = escapeshellcmd(dol_sanitizePathName($outputdir).'/'.dol_sanitizeFileName($fileinfo["filename"]));
2779 dol_delete_file($outputfilename.'.tmp');
2780 dol_delete_file($outputfilename.'.err');
2781
2782 $extension = strtolower(pathinfo($fileinfo["filename"], PATHINFO_EXTENSION));
2783 if ($extension == "tar") {
2784 $cmd = 'tar -C '.escapeshellcmd(dol_sanitizePathName($outputdir)).' -xvf '.escapeshellcmd(dol_sanitizePathName($fileinfo["dirname"]).'/'.dol_sanitizeFileName($fileinfo["basename"]));
2785
2786 $resarray = $utils->executeCLI($cmd, $outputfilename.'.tmp', 0, $outputfilename.'.err', 0);
2787 if ($resarray["result"] != 0) {
2788 $resarray["error"] .= file_get_contents($outputfilename.'.err');
2789 }
2790 } else {
2791 $program = "";
2792 if ($fileinfo["extension"] == "gz") {
2793 $program = 'gzip';
2794 } elseif ($fileinfo["extension"] == "bz2") {
2795 $program = 'bzip2';
2796 } elseif ($fileinfo["extension"] == "zst") {
2797 $program = 'zstd';
2798 } else {
2799 return array('error' => 'ErrorBadFileExtension');
2800 }
2801 $cmd = $program.' -dc '.escapeshellcmd(dol_sanitizePathName($fileinfo["dirname"]).'/'.dol_sanitizeFileName($fileinfo["basename"]));
2802 $cmd .= ' > '.$outputfilename;
2803
2804 $resarray = $utils->executeCLI($cmd, $outputfilename.'.tmp', 0, null, 1, $outputfilename.'.err');
2805 if ($resarray["result"] != 0) {
2806 $errfilecontent = @file_get_contents($outputfilename.'.err');
2807 if ($errfilecontent) {
2808 $resarray["error"] .= " - ".$errfilecontent;
2809 }
2810 }
2811 }
2812 return $resarray["result"] != 0 ? array('error' => $resarray["error"]) : array();
2813 }
2814
2815 return array('error' => 'ErrorBadFileExtension');
2816}
2817
2818
2831function dol_compress_dir($inputdir, $outputfile, $mode = "zip", $excludefiles = '', $rootdirinzip = '', $newmask = '0')
2832{
2833 $foundhandler = 0;
2834
2835 dol_syslog("Try to zip dir ".$inputdir." into ".$outputfile." mode=".$mode);
2836
2837 if (!dol_is_dir(dirname($outputfile)) || !is_writable(dirname($outputfile))) {
2838 global $langs, $errormsg;
2839 $langs->load("errors");
2840 $errormsg = $langs->trans("ErrorFailedToWriteInDir", $outputfile);
2841 return -3;
2842 }
2843
2844 try {
2845 if ($mode == 'gz') {
2846 $foundhandler = 0;
2847 } elseif ($mode == 'bz') {
2848 $foundhandler = 0;
2849 } elseif ($mode == 'zip') {
2850 /*if (defined('ODTPHP_PATHTOPCLZIP'))
2851 {
2852 $foundhandler=0; // TODO implement this
2853
2854 include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
2855 $archive = new PclZip($outputfile);
2856 $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
2857 //$archive->add($inputfile);
2858 return 1;
2859 }
2860 else*/
2861 //if (class_exists('ZipArchive') && !empty($conf->global->MAIN_USE_ZIPARCHIVE_FOR_ZIP_COMPRESS))
2862
2863 if (class_exists('ZipArchive')) {
2864 $foundhandler = 1;
2865
2866 // Initialize archive object
2867 $zip = new ZipArchive();
2868 $result = $zip->open($outputfile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
2869 if ($result !== true) {
2870 global $langs, $errormsg;
2871 $langs->load("errors");
2872 $errormsg = $langs->trans("ErrorFailedToBuildArchive", $outputfile);
2873 return -4;
2874 }
2875
2876 // Create recursive directory iterator
2877 // This does not return symbolic links
2879 $files = new RecursiveIteratorIterator(
2880 new RecursiveDirectoryIterator($inputdir, FilesystemIterator::UNIX_PATHS),
2881 RecursiveIteratorIterator::LEAVES_ONLY
2882 );
2883 '@phan-var-force SplFileInfo[] $files';
2884
2885 //var_dump($inputdir);
2886 foreach ($files as $name => $file) {
2887 // Skip directories (they would be added automatically)
2888 if (!$file->isDir()) {
2889 // Get real and relative path for current file
2890 $filePath = $file->getPath(); // the full path with filename using the $inputdir root.
2891 $fileName = $file->getFilename();
2892 $fileFullRealPath = $file->getRealPath(); // the full path with name and transformed to use real path directory.
2893
2894 //$relativePath = ($rootdirinzip ? $rootdirinzip.'/' : '').substr($fileFullRealPath, strlen($inputdir) + 1);
2895 $relativePath = ($rootdirinzip ? $rootdirinzip.'/' : '').substr(($filePath ? $filePath.'/' : '').$fileName, strlen($inputdir) + 1);
2896
2897 //var_dump($filePath);var_dump($fileFullRealPath);var_dump($relativePath);
2898 if (empty($excludefiles) || !preg_match($excludefiles, $fileFullRealPath)) {
2899 // Add current file to archive
2900 $zip->addFile($fileFullRealPath, $relativePath);
2901 }
2902 }
2903 }
2904
2905 // Zip archive will be created only after closing object
2906 $zip->close();
2907
2908 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
2909 $newmask = getDolGlobalString('MAIN_UMASK');
2910 }
2911 if (empty($newmask)) { // This should no happen
2912 dol_syslog("Warning: dol_compress_dir called with empty value for newmask and no default value defined", LOG_WARNING);
2913 $newmask = '0664';
2914 }
2915
2916 dolChmod($outputfile, $newmask);
2917
2918 return 1;
2919 }
2920 }
2921
2922 if (!$foundhandler) {
2923 dol_syslog("Try to zip with format ".$mode." with no handler for this format", LOG_ERR);
2924 return -2;
2925 } else {
2926 return 0;
2927 }
2928 } catch (Exception $e) {
2929 global $langs, $errormsg;
2930 $langs->load("errors");
2931 dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
2932 dol_syslog($e->getMessage(), LOG_ERR);
2933 $errormsg = $langs->trans("ErrorFailedToBuildArchive", $outputfile).' - '.$e->getMessage();
2934 return -1;
2935 }
2936}
2937
2938
2939
2950function dol_most_recent_file($dir, $regexfilter = '', $excludefilter = array('(\.meta|_preview.*\.png)$', '^\.'), $nohook = 0, $mode = 0)
2951{
2952 $tmparray = dol_dir_list($dir, 'files', 0, $regexfilter, $excludefilter, 'date', SORT_DESC, $mode, $nohook);
2953 return isset($tmparray[0]) ? $tmparray[0] : null;
2954}
2955
2969function dol_check_secure_access_document($modulepart, $original_file, $entity, $fuser = null, $refname = '', $mode = 'read')
2970{
2971 global $conf, $db, $user, $hookmanager;
2972 global $dolibarr_main_data_root, $dolibarr_main_document_root_alt;
2973 global $object;
2974
2975 if (!is_object($fuser)) {
2976 $fuser = $user;
2977 }
2978
2979 if (empty($modulepart)) {
2980 return 'ErrorBadParameter';
2981 }
2982 if (empty($entity)) {
2983 if (!isModEnabled('multicompany')) {
2984 $entity = 1;
2985 } else {
2986 $entity = 0;
2987 }
2988 }
2989 // Fix modulepart for backward compatibility
2990 if ($modulepart == 'facture') {
2991 $modulepart = 'invoice';
2992 } elseif ($modulepart == 'users') {
2993 $modulepart = 'user';
2994 } elseif ($modulepart == 'tva') {
2995 $modulepart = 'tax-vat';
2996 } elseif ($modulepart == 'expedition' && strpos($original_file, 'receipt/') === 0) {
2997 // Fix modulepart delivery
2998 $modulepart = 'delivery';
2999 } elseif ($modulepart == 'propale') {
3000 $modulepart = 'propal';
3001 }
3002
3003 //print 'dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity;
3004 dol_syslog('dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity);
3005
3006 // We define $accessallowed and $sqlprotectagainstexternals
3007 $accessallowed = 0;
3008 $sqlprotectagainstexternals = '';
3009 $ret = array();
3010
3011 // Find the subdirectory name as the reference. For example original_file='10/myfile.pdf' -> refname='10'
3012 if (empty($refname)) {
3013 $refname = basename(dirname($original_file)."/");
3014 if ($refname == 'thumbs' || $refname == 'temp') {
3015 // If we get the thumbs directory, we must go one step higher. For example original_file='10/thumbs/myfile_small.jpg' -> refname='10'
3016 $refname = basename(dirname(dirname($original_file))."/");
3017 }
3018 }
3019
3020 // Define possible keys to use for permission check
3021 $lire = 'lire';
3022 $read = 'read';
3023 $download = 'download';
3024 if ($mode == 'write') {
3025 $lire = 'creer';
3026 $read = 'write';
3027 $download = 'upload';
3028 }
3029
3030 // Wrapping for miscellaneous medias files
3031 if ($modulepart == 'common') {
3032 // Wrapping for some images
3033 $accessallowed = 1;
3034 $original_file = DOL_DOCUMENT_ROOT.'/public/theme/common/'.$original_file;
3035 } elseif ($modulepart == 'medias' && !empty($dolibarr_main_data_root)) {
3036 /* the medias directory is by default a public directory accessible online for everybody, so test on permission per entity has no sense
3037 if (isModEnabled('multicompany') && (empty($entity) || empty($conf->medias->multidir_output[$entity]))) {
3038 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3039 } */
3040 if (empty($entity)) {
3041 $entity = 1;
3042 }
3043 $accessallowed = 1;
3044 $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;
3045 } elseif ($modulepart == 'logs' && !empty($dolibarr_main_data_root)) {
3046 // Wrapping for *.log files, like when used with url http://.../document.php?modulepart=logs&file=dolibarr.log
3047 $accessallowed = ($user->admin && basename($original_file) == $original_file && preg_match('/^dolibarr.*\.(log|json)$/', basename($original_file)));
3048 $original_file = $dolibarr_main_data_root.'/'.$original_file;
3049 } elseif ($modulepart == 'doctemplates' && !empty($dolibarr_main_data_root)) {
3050 $accessallowed = $user->admin;
3051 $relative_file = $original_file;
3052 $ent = ($entity > 0 ? $entity : $conf->entity);
3053 $path_with_entity = $dolibarr_main_data_root . '/' . $ent . '/doctemplates/' . $relative_file;
3054 if ($ent > 1 && file_exists(dol_osencode($path_with_entity))) {
3055 $original_file = $path_with_entity;
3056 } else {
3057 $original_file = $dolibarr_main_data_root . '/doctemplates/' . $relative_file;
3058 }
3059 } elseif ($modulepart == 'doctemplateswebsite' && !empty($dolibarr_main_data_root)) {
3060 // Wrapping for doctemplates of websites
3061 $accessallowed = ($fuser->hasRight('website', 'write') && preg_match('/\.jpg$/i', basename($original_file)));
3062 $original_file = $dolibarr_main_data_root.'/doctemplates/websites/'.$original_file;
3063 } elseif ($modulepart == 'packages' && !empty($dolibarr_main_data_root)) { // To download zip of modules
3064 // Wrapping for *.zip package files, like when used with url http://.../document.php?modulepart=packages&file=module_myfile.zip
3065 // Dir for custom dirs
3066 $tmp = explode(',', $dolibarr_main_document_root_alt);
3067 $dirins = $tmp[0];
3068
3069 $accessallowed = ($user->admin && preg_match('/^module_.*\.zip$/', basename($original_file)));
3070 $original_file = $dirins.'/'.$original_file;
3071 } elseif ($modulepart == 'mycompany' && !empty($conf->mycompany->dir_output)) {
3072 // Wrapping for some images
3073 $accessallowed = 1;
3074 $original_file = $conf->mycompany->dir_output.'/'.$original_file;
3075 } elseif ($modulepart == 'userphoto' && !empty($conf->user->dir_output)) {
3076 // Wrapping for users photos (user photos are allowed to any connected users)
3077 $accessallowed = 0;
3078 if (preg_match('/^\d+\/photos\//', $original_file)) {
3079 $accessallowed = 1;
3080 }
3081 $original_file = $conf->user->dir_output.'/'.$original_file;
3082 } elseif ($modulepart == 'userphotopublic' && !empty($conf->user->dir_output)) {
3083 // Wrapping for users photos that were set to public (for virtual credit card) by their owner (public user photos can be read
3084 // with the public link and securekey)
3085 $accessok = false;
3086 $reg = array();
3087 if (preg_match('/^(\d+)\/photos\//', $original_file, $reg)) {
3088 if ((int) $reg[1]) {
3089 $tmpobject = new User($db);
3090 $tmpobject->fetch((int) $reg[1], '', '', 1);
3091 if (getDolUserInt('USER_ENABLE_PUBLIC', 0, $tmpobject)) {
3092 $securekey = GETPOST('securekey', 'alpha', 1);
3093 // Security check
3094 global $dolibarr_main_cookie_cryptkey, $dolibarr_main_instance_unique_id;
3095 $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
3096 $encodedsecurekey = dol_hash($valuetouse.'uservirtualcard'.$tmpobject->id.'-'.$tmpobject->login, 'md5');
3097 if ($encodedsecurekey == $securekey) {
3098 $accessok = true;
3099 }
3100 }
3101 }
3102 }
3103 if ($accessok) {
3104 $accessallowed = 1;
3105 }
3106 $original_file = $conf->user->dir_output.'/'.$original_file;
3107 } elseif (($modulepart == 'companylogo') && !empty($conf->mycompany->dir_output)) {
3108 // Wrapping for company logos (company logos are allowed to anyboby, they are public)
3109 $accessallowed = 1;
3110 $original_file = $conf->mycompany->dir_output.'/logos/'.$original_file;
3111 } elseif ($modulepart == 'memberphoto' && !empty($conf->member->dir_output)) {
3112 // Wrapping for members photos
3113 $accessallowed = 0;
3114 // Simple chosen for automatic generation of member codes
3115 if (preg_match('/^\d+\/photos\//', $original_file)) {
3116 $accessallowed = 1;
3117 }
3118 // Advanced chosen for automatic generation of member codes
3119 if (preg_match('/^MEM\d\d\d\d-\d\d\d\d\/photos\//', $original_file)) {
3120 $accessallowed = 1;
3121 }
3122 $original_file = $conf->member->dir_output.'/'.$original_file;
3123 } elseif ($modulepart == 'apercufacture' && !empty($conf->invoice->multidir_output[$entity])) {
3124 // Wrapping for invoices (user need permission to read invoices)
3125 if ($fuser->hasRight('facture', $lire)) {
3126 $accessallowed = 1;
3127 }
3128 $original_file = $conf->invoice->multidir_output[$entity].'/'.$original_file;
3129 } elseif ($modulepart == 'apercupropal' && !empty($conf->propal->multidir_output[$entity])) {
3130 // Wrapping pour les apercu propal
3131 if ($fuser->hasRight('propal', $lire)) {
3132 $accessallowed = 1;
3133 }
3134 $original_file = $conf->propal->multidir_output[$entity].'/'.$original_file;
3135 } elseif ($modulepart == 'apercucommande' && !empty($conf->order->multidir_output[$entity])) {
3136 // Wrapping pour les apercu commande
3137 if ($fuser->hasRight('commande', $lire)) {
3138 $accessallowed = 1;
3139 }
3140 $original_file = $conf->order->multidir_output[$entity].'/'.$original_file;
3141 } elseif (($modulepart == 'apercufichinter' || $modulepart == 'apercuficheinter') && !empty($conf->ficheinter->dir_output)) {
3142 // Wrapping pour les apercu intervention
3143 if ($fuser->hasRight('ficheinter', $lire)) {
3144 $accessallowed = 1;
3145 }
3146 $original_file = $conf->ficheinter->dir_output.'/'.$original_file;
3147 } elseif (($modulepart == 'apercucontract') && !empty($conf->contract->multidir_output[$entity])) {
3148 // Wrapping pour les apercu contrat
3149 if ($fuser->hasRight('contrat', $lire)) {
3150 $accessallowed = 1;
3151 }
3152 $original_file = $conf->contract->multidir_output[$entity].'/'.$original_file;
3153 } elseif (($modulepart == 'apercusupplier_proposal') && !empty($conf->supplier_proposal->dir_output)) {
3154 // Wrapping pour les apercu supplier proposal
3155 if ($fuser->hasRight('supplier_proposal', $lire)) {
3156 $accessallowed = 1;
3157 }
3158 $original_file = $conf->supplier_proposal->dir_output.'/'.$original_file;
3159 } elseif (($modulepart == 'apercusupplier_order') && !empty($conf->fournisseur->commande->dir_output)) {
3160 // Wrapping pour les apercu supplier order
3161 if ($fuser->hasRight('fournisseur', 'commande', $lire)) {
3162 $accessallowed = 1;
3163 }
3164 $original_file = $conf->fournisseur->commande->dir_output.'/'.$original_file;
3165 } elseif (($modulepart == 'apercusupplier_invoice') && !empty($conf->fournisseur->facture->dir_output)) {
3166 // Wrapping pour les apercu supplier invoice
3167 if ($fuser->hasRight('fournisseur', $lire)) {
3168 $accessallowed = 1;
3169 }
3170 $original_file = $conf->fournisseur->facture->dir_output.'/'.$original_file;
3171 } elseif (($modulepart == 'holiday') && !empty($conf->holiday->dir_output)) {
3172 if ($fuser->hasRight('holiday', $read) || $fuser->hasRight('holiday', 'readall') || preg_match('/^specimen/i', $original_file)) {
3173 $accessallowed = 1;
3174 // If we known $id of holiday, call checkUserAccessToObject to check permission on properties and hierarchy of leave request
3175 if ($refname && !$fuser->hasRight('holiday', 'readall') && !preg_match('/^specimen/i', $original_file)) {
3176 include_once DOL_DOCUMENT_ROOT.'/holiday/class/holiday.class.php';
3177 $tmpholiday = new Holiday($db);
3178 $tmpholiday->fetch(0, $refname);
3179 $accessallowed = checkUserAccessToObject($user, array('holiday'), $tmpholiday, 'holiday', '', '', 'rowid', '');
3180 }
3181 }
3182 $original_file = $conf->holiday->dir_output.'/'.$original_file;
3183 } elseif (($modulepart == 'expensereport') && !empty($conf->expensereport->dir_output)) {
3184 if ($fuser->hasRight('expensereport', $lire) || $fuser->hasRight('expensereport', 'readall') || preg_match('/^specimen/i', $original_file)) {
3185 $accessallowed = 1;
3186 // If we known $id of expensereport, call checkUserAccessToObject to check permission on properties and hierarchy of expense report
3187 if ($refname && !$fuser->hasRight('expensereport', 'readall') && !preg_match('/^specimen/i', $original_file)) {
3188 include_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
3189 $tmpexpensereport = new ExpenseReport($db);
3190 $tmpexpensereport->fetch(0, $refname);
3191 $accessallowed = checkUserAccessToObject($user, array('expensereport'), $tmpexpensereport, 'expensereport', '', '', 'rowid', '');
3192 }
3193 }
3194 $original_file = $conf->expensereport->dir_output.'/'.$original_file;
3195 } elseif (($modulepart == 'apercuexpensereport') && !empty($conf->expensereport->dir_output)) {
3196 // Wrapping pour les apercu expense report
3197 if ($fuser->hasRight('expensereport', $lire)) {
3198 $accessallowed = 1;
3199 }
3200 $original_file = $conf->expensereport->dir_output.'/'.$original_file;
3201 } elseif ($modulepart == 'propalstats' && !empty($conf->propal->multidir_temp[$entity])) {
3202 // Wrapping pour les images des stats propales
3203 if ($fuser->hasRight('propal', $lire)) {
3204 $accessallowed = 1;
3205 }
3206 $original_file = $conf->propal->multidir_temp[$entity].'/'.$original_file;
3207 } elseif ($modulepart == 'orderstats' && !empty($conf->order->dir_temp)) {
3208 // Wrapping pour les images des stats commandes
3209 if ($fuser->hasRight('commande', $lire)) {
3210 $accessallowed = 1;
3211 }
3212 $original_file = $conf->order->dir_temp.'/'.$original_file;
3213 } elseif ($modulepart == 'orderstatssupplier' && !empty($conf->fournisseur->dir_output)) {
3214 if ($fuser->hasRight('fournisseur', 'commande', $lire)) {
3215 $accessallowed = 1;
3216 }
3217 $original_file = $conf->fournisseur->commande->dir_temp.'/'.$original_file;
3218 } elseif ($modulepart == 'billstats' && !empty($conf->invoice->dir_temp)) {
3219 // Wrapping pour les images des stats factures
3220 if ($fuser->hasRight('facture', $lire)) {
3221 $accessallowed = 1;
3222 }
3223 $original_file = $conf->invoice->dir_temp.'/'.$original_file;
3224 } elseif ($modulepart == 'billstatssupplier' && !empty($conf->fournisseur->dir_output)) {
3225 if ($fuser->hasRight('fournisseur', 'facture', $lire)) {
3226 $accessallowed = 1;
3227 }
3228 $original_file = $conf->fournisseur->facture->dir_temp.'/'.$original_file;
3229 } elseif ($modulepart == 'expeditionstats' && !empty($conf->expedition->dir_temp)) {
3230 // Wrapping pour les images des stats expeditions
3231 if ($fuser->hasRight('expedition', $lire)) {
3232 $accessallowed = 1;
3233 }
3234 $original_file = $conf->expedition->dir_temp.'/'.$original_file;
3235 } elseif ($modulepart == 'tripsexpensesstats' && !empty($conf->deplacement->dir_temp)) {
3236 // Wrapping pour les images des stats expeditions
3237 if ($fuser->hasRight('deplacement', $lire)) {
3238 $accessallowed = 1;
3239 }
3240 $original_file = $conf->deplacement->dir_temp.'/'.$original_file;
3241 } elseif ($modulepart == 'memberstats' && !empty($conf->member->dir_temp)) {
3242 // Wrapping pour les images des stats expeditions
3243 if ($fuser->hasRight('adherent', $lire)) {
3244 $accessallowed = 1;
3245 }
3246 $original_file = $conf->member->dir_temp.'/'.$original_file;
3247 } elseif (preg_match('/^productstats_/i', $modulepart) && !empty($conf->product->dir_temp)) {
3248 // Wrapping pour les images des stats produits
3249 if ($fuser->hasRight('produit', $lire) || $fuser->hasRight('service', $lire)) {
3250 $accessallowed = 1;
3251 }
3252 $original_file = (!empty($conf->product->multidir_temp[$entity]) ? $conf->product->multidir_temp[$entity] : $conf->service->multidir_temp[$entity]).'/'.$original_file;
3253 } elseif (in_array($modulepart, array('tax', 'tax-vat', 'tva')) && !empty($conf->tax->dir_output)) {
3254 // Wrapping for taxes
3255 if ($fuser->hasRight('tax', 'charges', $lire)) {
3256 $accessallowed = 1;
3257 }
3258 $modulepartsuffix = str_replace('tax-', '', $modulepart);
3259 $original_file = $conf->tax->dir_output.'/'.($modulepartsuffix != 'tax' ? $modulepartsuffix.'/' : '').$original_file;
3260 } elseif ($modulepart == 'actions' && !empty($conf->agenda->dir_output)) {
3261 // Wrapping for events
3262 if ($fuser->hasRight('agenda', 'myactions', $read)) {
3263 $accessallowed = 1;
3264 // If we known $id of project, call checkUserAccessToObject to check permission on the given agenda event on properties and assigned users
3265 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3266 include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3267 $tmpobject = new ActionComm($db);
3268 $tmpobject->fetch((int) $refname);
3269 $accessallowed = checkUserAccessToObject($user, array('agenda'), $tmpobject->id, 'actioncomm&societe', 'myactions|allactions', 'fk_soc', 'id', '');
3270 if ($user->socid && $tmpobject->socid) {
3271 $accessallowed = checkUserAccessToObject($user, array('societe'), $tmpobject->socid);
3272 }
3273 }
3274 }
3275 $original_file = $conf->agenda->dir_output.'/'.$original_file;
3276 } elseif ($modulepart == 'category' && !empty($conf->categorie->multidir_output[$entity])) {
3277 // Wrapping for categories (categories are allowed if user has permission to read categories or to work on TakePos)
3278 if (empty($entity) || empty($conf->categorie->multidir_output[$entity])) {
3279 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3280 }
3281 if ($fuser->hasRight("categorie", $lire) || $fuser->hasRight("takepos", "run")) {
3282 $accessallowed = 1;
3283 }
3284 $original_file = $conf->categorie->multidir_output[$entity].'/'.$original_file;
3285 } elseif ($modulepart == 'prelevement' && !empty($conf->prelevement->dir_output)) {
3286 // Wrapping pour les prelevements
3287 if ($fuser->hasRight('prelevement', 'bons', $lire) || preg_match('/^specimen/i', $original_file)) {
3288 $accessallowed = 1;
3289 }
3290 $original_file = $conf->prelevement->dir_output.'/'.$original_file;
3291 } elseif ($modulepart == 'graph_stock' && !empty($conf->stock->dir_temp)) {
3292 // Wrapping pour les graph energie
3293 $accessallowed = 1;
3294 $original_file = $conf->stock->dir_temp.'/'.$original_file;
3295 } elseif ($modulepart == 'graph_fourn' && !empty($conf->fournisseur->dir_temp)) {
3296 // Wrapping pour les graph fournisseurs
3297 $accessallowed = 1;
3298 $original_file = $conf->fournisseur->dir_temp.'/'.$original_file;
3299 } elseif ($modulepart == 'graph_product' && !empty($conf->product->dir_temp)) {
3300 // Wrapping pour les graph des produits
3301 $accessallowed = 1;
3302 $original_file = $conf->product->multidir_temp[$entity].'/'.$original_file;
3303 } elseif ($modulepart == 'barcode') {
3304 // Wrapping pour les code barre
3305 $accessallowed = 1;
3306 // If viewimage is called for barcode, we try to output an image on the fly, with no build of file on disk.
3307 //$original_file=$conf->barcode->dir_temp.'/'.$original_file;
3308 $original_file = '';
3309 } elseif ($modulepart == 'iconmailing' && !empty($conf->mailing->dir_temp)) {
3310 // Wrapping for icon of background of mailings
3311 $accessallowed = 1;
3312 $original_file = $conf->mailing->dir_temp.'/'.$original_file;
3313 } elseif ($modulepart == 'scanner_user_temp' && !empty($conf->scanner->dir_temp)) {
3314 // Wrapping pour le scanner
3315 $accessallowed = 1;
3316 $original_file = $conf->scanner->dir_temp.'/'.$fuser->id.'/'.$original_file;
3317 } elseif ($modulepart == 'fckeditor' && !empty($conf->fckeditor->dir_output)) {
3318 // Wrapping pour les images fckeditor
3319 $accessallowed = 1;
3320 $original_file = $conf->fckeditor->dir_output.'/'.$original_file;
3321 } elseif ($modulepart == 'user' && !empty($conf->user->dir_output)) {
3322 // Wrapping for users
3323 $canreaduser = (!empty($fuser->admin) || $fuser->hasRight('user', 'user', $lire));
3324 if ($fuser->id == (int) $refname) {
3325 $canreaduser = 1;
3326 } // A user can always read its own card
3327 if ($canreaduser || preg_match('/^specimen/i', $original_file)) {
3328 $accessallowed = 1;
3329 }
3330 $original_file = $conf->user->dir_output.'/'.$original_file;
3331 } elseif (($modulepart == 'company' || $modulepart == 'societe' || $modulepart == 'thirdparty') && !empty($conf->societe->multidir_output[$entity])) {
3332 // Wrapping for third parties
3333 if (empty($entity) || empty($conf->societe->multidir_output[$entity])) {
3334 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3335 }
3336 if ($fuser->hasRight('societe', $lire) || preg_match('/^specimen/i', $original_file)) {
3337 $accessallowed = 1;
3338 }
3339 $original_file = $conf->societe->multidir_output[$entity].'/'.$original_file;
3340 $sqlprotectagainstexternals = "SELECT rowid as fk_soc FROM ".MAIN_DB_PREFIX."societe WHERE rowid='".$db->escape($refname)."' AND entity IN (".getEntity('societe').")";
3341 } elseif ($modulepart == 'contact' && !empty($conf->societe->multidir_output[$entity])) {
3342 // Wrapping for contact
3343 if (empty($entity) || empty($conf->societe->multidir_output[$entity])) {
3344 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3345 }
3346 if ($fuser->hasRight('societe', $lire)) {
3347 $accessallowed = 1;
3348 }
3349 $original_file = $conf->societe->multidir_output[$entity].'/contact/'.$original_file;
3350 } elseif (($modulepart == 'facture' || $modulepart == 'invoice') && !empty($conf->invoice->multidir_output[$entity])) {
3351 // Wrapping for invoices
3352 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3353 $accessallowed = 1;
3354 }
3355 $original_file = $conf->invoice->multidir_output[$entity].'/'.$original_file;
3356 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('invoice').")";
3357 } elseif ($modulepart == 'massfilesarea_proposals' && !empty($conf->propal->multidir_output[$entity])) {
3358 // Wrapping for mass actions
3359 if ($fuser->hasRight('propal', $lire) || preg_match('/^specimen/i', $original_file)) {
3360 $accessallowed = 1;
3361 }
3362 $original_file = $conf->propal->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3363 } elseif ($modulepart == 'massfilesarea_orders') {
3364 if ($fuser->hasRight('commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3365 $accessallowed = 1;
3366 }
3367 $original_file = $conf->order->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3368 } elseif ($modulepart == 'massfilesarea_sendings') {
3369 if ($fuser->hasRight('expedition', $lire) || preg_match('/^specimen/i', $original_file)) {
3370 $accessallowed = 1;
3371 }
3372 $original_file = $conf->expedition->dir_output.'/sending/temp/massgeneration/'.$user->id.'/'.$original_file;
3373 } elseif ($modulepart == 'massfilesarea_receipts') {
3374 if ($fuser->hasRight('reception', $lire) || preg_match('/^specimen/i', $original_file)) {
3375 $accessallowed = 1;
3376 }
3377 $original_file = $conf->reception->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3378 } elseif ($modulepart == 'massfilesarea_invoices') {
3379 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3380 $accessallowed = 1;
3381 }
3382 $original_file = $conf->invoice->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3383 } elseif ($modulepart == 'massfilesarea_expensereport') {
3384 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3385 $accessallowed = 1;
3386 }
3387 $original_file = $conf->expensereport->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3388 } elseif ($modulepart == 'massfilesarea_interventions') {
3389 if ($fuser->hasRight('ficheinter', $lire) || preg_match('/^specimen/i', $original_file)) {
3390 $accessallowed = 1;
3391 }
3392 $original_file = $conf->ficheinter->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3393 } elseif ($modulepart == 'massfilesarea_supplier_proposal' && !empty($conf->supplier_proposal->dir_output)) {
3394 if ($fuser->hasRight('supplier_proposal', $lire) || preg_match('/^specimen/i', $original_file)) {
3395 $accessallowed = 1;
3396 }
3397 $original_file = $conf->supplier_proposal->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3398 } elseif ($modulepart == 'massfilesarea_supplier_order') {
3399 if ($fuser->hasRight('fournisseur', 'commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3400 $accessallowed = 1;
3401 }
3402 $original_file = $conf->fournisseur->commande->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3403 } elseif ($modulepart == 'massfilesarea_supplier_invoice') {
3404 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3405 $accessallowed = 1;
3406 }
3407 $original_file = $conf->fournisseur->facture->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3408 } elseif ($modulepart == 'massfilesarea_contract' && !empty($conf->contract->dir_output)) {
3409 if ($fuser->hasRight('contrat', $lire) || preg_match('/^specimen/i', $original_file)) {
3410 $accessallowed = 1;
3411 }
3412 $original_file = $conf->contract->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3413 } elseif (($modulepart == 'fichinter' || $modulepart == 'ficheinter') && !empty($conf->ficheinter->dir_output)) {
3414 // Wrapping for interventions
3415 if ($fuser->hasRight('ficheinter', $lire) || preg_match('/^specimen/i', $original_file)) {
3416 $accessallowed = 1;
3417 }
3418 $original_file = $conf->ficheinter->dir_output.'/'.$original_file;
3419 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
3420 } elseif ($modulepart == 'deplacement' && !empty($conf->deplacement->dir_output)) {
3421 // Wrapping pour les deplacements et notes de frais
3422 if ($fuser->hasRight('deplacement', $lire) || preg_match('/^specimen/i', $original_file)) {
3423 $accessallowed = 1;
3424 }
3425 $original_file = $conf->deplacement->dir_output.'/'.$original_file;
3426 //$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
3427 } elseif (($modulepart == 'propal' || $modulepart == 'propale') && isset($conf->propal->multidir_output[$entity])) {
3428 // Wrapping pour les propales
3429 if ($fuser->hasRight('propal', $lire) || preg_match('/^specimen/i', $original_file)) {
3430 $accessallowed = 1;
3431 }
3432 $original_file = $conf->propal->multidir_output[$entity].'/'.$original_file;
3433 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."propal WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('propal').")";
3434 } elseif (($modulepart == 'commande' || $modulepart == 'order') && !empty($conf->order->multidir_output[$entity])) {
3435 // Wrapping pour les commandes
3436 if ($fuser->hasRight('commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3437 $accessallowed = 1;
3438 }
3439 $original_file = $conf->order->multidir_output[$entity].'/'.$original_file;
3440 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('order').")";
3441 } elseif ($modulepart == 'project' && !empty($conf->project->multidir_output[$entity])) {
3442 // Wrapping pour les projects
3443 if ($fuser->hasRight('projet', $lire) || preg_match('/^specimen/i', $original_file)) {
3444 $accessallowed = 1;
3445 // If we known $id of project, call checkUserAccessToObject to check permission on properties and contact of project
3446 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3447 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
3448 $tmpproject = new Project($db);
3449 $tmpproject->fetch(0, $refname);
3450 $accessallowed = checkUserAccessToObject($user, array('projet'), $tmpproject->id, 'projet&project', '', '', 'rowid', '');
3451 }
3452 }
3453 $original_file = $conf->project->multidir_output[$entity].'/'.$original_file;
3454 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
3455 } elseif ($modulepart == 'project_task' && !empty($conf->project->multidir_output[$entity])) {
3456 if ($fuser->hasRight('projet', $lire) || preg_match('/^specimen/i', $original_file)) {
3457 $accessallowed = 1;
3458 // If we known $id of project, call checkUserAccessToObject to check permission on properties and contact of project
3459 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3460 include_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
3461 $tmptask = new Task($db);
3462 $tmptask->fetch(0, $refname);
3463 $accessallowed = checkUserAccessToObject($user, array('projet_task'), $tmptask->id, 'projet_task&project', '', '', 'rowid', '');
3464 }
3465 }
3466 $original_file = $conf->project->multidir_output[$entity].'/'.$original_file;
3467 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
3468 } elseif (($modulepart == 'commande_fournisseur' || $modulepart == 'order_supplier') && !empty($conf->fournisseur->commande->dir_output)) {
3469 // Wrapping pour les commandes fournisseurs
3470 if ($fuser->hasRight('fournisseur', 'commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3471 $accessallowed = 1;
3472 }
3473 $original_file = $conf->fournisseur->commande->dir_output.'/'.$original_file;
3474 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
3475 } elseif (($modulepart == 'facture_fournisseur' || $modulepart == 'invoice_supplier') && !empty($conf->fournisseur->facture->dir_output)) {
3476 // Wrapping pour les factures fournisseurs
3477 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3478 $accessallowed = 1;
3479 }
3480 $original_file = $conf->fournisseur->facture->dir_output.'/'.$original_file;
3481 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture_fourn WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
3482 } elseif ($modulepart == 'supplier_payment') {
3483 // Wrapping pour les rapport de paiements
3484 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3485 $accessallowed = 1;
3486 }
3487 $original_file = preg_replace("/payment\//", "", $original_file); // Because the $conf->fournisseur->payment->dir_output already contains the "payment/"
3488 $original_file = $conf->fournisseur->payment->dir_output.'/'.$original_file;
3489 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."paiementfournisseur WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
3490 } elseif ($modulepart == 'payment') {
3491 // Wrapping pour les rapport de paiements
3492 if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i', $original_file)) {
3493 $accessallowed = 1;
3494 }
3495 $original_file = $conf->compta->payment->dir_output.'/'.$original_file;
3496 } elseif ($modulepart == 'facture_paiement' && !empty($conf->invoice->dir_output)) {
3497 // Wrapping pour les rapport de paiements
3498 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3499 $accessallowed = 1;
3500 }
3501 if ($fuser->socid > 0) {
3502 $original_file = $conf->invoice->dir_output.'/payments/private/'.$fuser->id.'/'.$original_file;
3503 } else {
3504 $original_file = $conf->invoice->dir_output.'/payments/'.$original_file;
3505 }
3506 } elseif ($modulepart == 'export_compta' && !empty($conf->accounting->dir_output)) {
3507 // Wrapping for accounting exports
3508 if ($fuser->hasRight('accounting', 'bind', 'write') || preg_match('/^specimen/i', $original_file)) {
3509 $accessallowed = 1;
3510 }
3511 $original_file = $conf->accounting->dir_output.'/'.$original_file;
3512 } elseif (($modulepart == 'expedition' || $modulepart == 'shipment') && !empty($conf->expedition->dir_output)) {
3513 // Wrapping pour les expedition
3514 if ($fuser->hasRight('expedition', $lire) || preg_match('/^specimen/i', $original_file)) {
3515 $accessallowed = 1;
3516 }
3517 $original_file = $conf->expedition->dir_output."/".(strpos($original_file, 'sending/') === 0 ? '' : 'sending/').$original_file;
3518 //$original_file = $conf->expedition->dir_output."/".$original_file;
3519 } elseif (($modulepart == 'livraison' || $modulepart == 'delivery') && !empty($conf->expedition->dir_output)) {
3520 // Delivery Note Wrapping
3521 if ($fuser->hasRight('expedition', 'delivery', $lire) || preg_match('/^specimen/i', $original_file)) {
3522 $accessallowed = 1;
3523 }
3524 $original_file = $conf->expedition->dir_output."/".(strpos($original_file, 'receipt/') === 0 ? '' : 'receipt/').$original_file;
3525 } elseif ($modulepart == 'actionsreport' && !empty($conf->agenda->dir_temp)) {
3526 // Wrapping pour les actions
3527 if ($fuser->hasRight('agenda', 'allactions', $read) || preg_match('/^specimen/i', $original_file)) {
3528 $accessallowed = 1;
3529 }
3530 $original_file = $conf->agenda->dir_temp."/".$original_file;
3531 } elseif ($modulepart == 'product' || $modulepart == 'produit' || $modulepart == 'service' || $modulepart == 'produit|service') {
3532 // Wrapping pour les produits et services
3533 if (empty($entity) || (empty($conf->product->multidir_output[$entity]) && empty($conf->service->multidir_output[$entity]))) {
3534 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3535 }
3536 if (($fuser->hasRight('produit', $lire) || $fuser->hasRight('service', $lire)) || preg_match('/^specimen/i', $original_file)) {
3537 $accessallowed = 1;
3538 }
3539 if (isModEnabled("product")) {
3540 $original_file = $conf->product->multidir_output[$entity].'/'.$original_file;
3541 } elseif (isModEnabled("service")) {
3542 $original_file = $conf->service->multidir_output[$entity].'/'.$original_file;
3543 }
3544 } elseif ($modulepart == 'product_batch' || $modulepart == 'produitlot') {
3545 // Wrapping pour les lots produits
3546 if (empty($entity) || (empty($conf->productbatch->multidir_output[$entity]))) {
3547 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3548 }
3549 if (($fuser->hasRight('produit', $lire)) || preg_match('/^specimen/i', $original_file)) {
3550 $accessallowed = 1;
3551 }
3552 if (isModEnabled('productbatch')) {
3553 $original_file = $conf->productbatch->multidir_output[$entity].'/'.$original_file;
3554 }
3555 } elseif ($modulepart == 'movement' || $modulepart == 'mouvement') {
3556 // Wrapping for stock movements
3557 if (empty($entity) || empty($conf->stock->multidir_output[$entity])) {
3558 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3559 }
3560 if (($fuser->hasRight('stock', $lire) || $fuser->hasRight('stock', 'movement', $lire) || $fuser->hasRight('stock', 'mouvement', $lire)) || preg_match('/^specimen/i', $original_file)) {
3561 $accessallowed = 1;
3562 }
3563 if (isModEnabled('stock')) {
3564 $original_file = $conf->stock->multidir_output[$entity].'/movement/'.$original_file;
3565 }
3566 } elseif ($modulepart == 'contract' && !empty($conf->contract->multidir_output[$entity])) {
3567 // Wrapping pour les contrats
3568 if ($fuser->hasRight('contrat', $lire) || preg_match('/^specimen/i', $original_file)) {
3569 $accessallowed = 1;
3570 }
3571 $original_file = $conf->contract->multidir_output[$entity].'/'.$original_file;
3572 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."contrat WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('contract').")";
3573 } elseif ($modulepart == 'donation' && !empty($conf->don->dir_output)) {
3574 // Wrapping pour les dons
3575 if ($fuser->hasRight('don', $lire) || preg_match('/^specimen/i', $original_file)) {
3576 $accessallowed = 1;
3577 }
3578 $original_file = $conf->don->dir_output.'/'.$original_file;
3579 } elseif ($modulepart == 'dolresource' && !empty($conf->resource->dir_output)) {
3580 // Wrapping pour les dons
3581 if ($fuser->hasRight('resource', $read) || preg_match('/^specimen/i', $original_file)) {
3582 $accessallowed = 1;
3583 }
3584 $original_file = $conf->resource->dir_output.'/'.$original_file;
3585 } elseif (($modulepart == 'remisecheque' || $modulepart == 'chequereceipt') && !empty($conf->bank->dir_output)) {
3586 // Wrapping pour les remises de cheques
3587 if ($fuser->hasRight('banque', $lire) || preg_match('/^specimen/i', $original_file)) {
3588 $accessallowed = 1;
3589 }
3590 $original_file = $conf->bank->dir_output.'/checkdeposits/'.$original_file; // original_file should contains relative path so include the get_exdir result
3591 } elseif (($modulepart == 'banque' || $modulepart == 'bank') && !empty($conf->bank->dir_output)) {
3592 // Wrapping for bank
3593 if ($fuser->hasRight('banque', $lire)) {
3594 $accessallowed = 1;
3595 }
3596 $original_file = $conf->bank->dir_output.'/'.$original_file;
3597 } elseif ($modulepart == 'export' && !empty($conf->export->dir_temp)) {
3598 // Wrapping for export module
3599 // Note that a test may not be required because we force the dir of download on the directory of the user that export
3600 $accessallowed = $user->hasRight('export', 'lire');
3601 $original_file = $conf->export->dir_temp.'/'.$fuser->id.'/'.$original_file;
3602 } elseif ($modulepart == 'import' && !empty($conf->import->dir_temp)) {
3603 // Wrapping for import module
3604 $accessallowed = $user->hasRight('import', 'run');
3605 $original_file = $conf->import->dir_temp.'/'.$original_file;
3606 } elseif ($modulepart == 'recruitment' && !empty($conf->recruitment->dir_output)) {
3607 // Wrapping for recruitment module
3608 $accessallowed = $user->hasRight('recruitment', 'recruitmentjobposition', 'read');
3609 $original_file = $conf->recruitment->dir_output.'/'.$original_file;
3610 } elseif ($modulepart == 'hrm' && !empty($conf->hrm->dir_output)) {
3611 // Wrapping for hrm module
3612 $accessallowed = $user->hasRight('hrm', 'all', 'read');
3613 $original_file = $conf->hrm->dir_output.'/'.$original_file;
3614 } elseif ($modulepart == 'editor' && !empty($conf->fckeditor->dir_output)) {
3615 // Wrapping for wysiwyg editor
3616 $accessallowed = 1;
3617 $original_file = $conf->fckeditor->dir_output.'/'.$original_file;
3618 } elseif ($modulepart == 'systemtools' && !empty($conf->admin->dir_output)) {
3619 // Wrapping for backups
3620 if ($fuser->admin) {
3621 $accessallowed = 1;
3622 }
3623 $original_file = $conf->admin->dir_output.'/'.$original_file;
3624 } elseif ($modulepart == 'admin_temp' && !empty($conf->admin->dir_temp)) {
3625 // Wrapping for upload file test
3626 if ($fuser->admin) {
3627 $accessallowed = 1;
3628 }
3629 $original_file = $conf->admin->dir_temp.'/'.$original_file;
3630 } elseif ($modulepart == 'bittorrent' && !empty($conf->bittorrent->dir_output)) {
3631 // Wrapping pour BitTorrent
3632 $accessallowed = 1;
3633 $dir = 'files';
3634 if (dol_mimetype($original_file) == 'application/x-bittorrent') {
3635 $dir = 'torrents';
3636 }
3637 $original_file = $conf->bittorrent->dir_output.'/'.$dir.'/'.$original_file;
3638 } elseif ($modulepart == 'member' && !empty($conf->member->dir_output)) {
3639 // Wrapping pour Foundation module
3640 if ($fuser->hasRight('adherent', $lire) || preg_match('/^specimen/i', $original_file)) {
3641 $accessallowed = 1;
3642 }
3643 $original_file = $conf->member->dir_output.'/'.$original_file;
3644 // If modulepart=module_user_temp Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp/iduser
3645 // If modulepart=module_temp Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp
3646 // If modulepart=module_user Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/iduser
3647 // If modulepart=module Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart
3648 // If modulepart=module-abc Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart
3649 } else {
3650 // GENERIC Wrapping
3651 //var_dump($modulepart);
3652 //var_dump($original_file);
3653 if (preg_match('/^specimen/i', $original_file)) {
3654 $accessallowed = 1; // If link to a file called specimen. Test must be done before changing $original_file int full path.
3655 }
3656 if ($fuser->admin) {
3657 $accessallowed = 1; // If user is admin
3658 }
3659
3660 $tmpmodulepart = explode('-', $modulepart);
3661 if (!empty($tmpmodulepart[1])) {
3662 $modulepart = $tmpmodulepart[0];
3663 $original_file = $tmpmodulepart[1].'/'.$original_file;
3664 }
3665
3666 // Define $accessallowed
3667 $reg = array();
3668 if (preg_match('/^([a-z]+)_user_temp$/i', $modulepart, $reg)) {
3669 $tmpmodule = $reg[1];
3670 if (empty($conf->$tmpmodule->dir_temp)) { // modulepart not supported
3671 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3672 exit;
3673 }
3674 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3675 $accessallowed = 1;
3676 }
3677 $original_file = $conf->{$reg[1]}->dir_temp.'/'.$fuser->id.'/'.$original_file;
3678 } elseif (preg_match('/^([a-z]+)_temp$/i', $modulepart, $reg)) {
3679 $tmpmodule = $reg[1];
3680 if (empty($conf->$tmpmodule->dir_temp)) { // modulepart not supported
3681 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3682 exit;
3683 }
3684 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3685 $accessallowed = 1;
3686 }
3687 $original_file = $conf->$tmpmodule->dir_temp.'/'.$original_file;
3688 } elseif (preg_match('/^([a-z]+)_user$/i', $modulepart, $reg)) {
3689 $tmpmodule = $reg[1];
3690 if (empty($conf->$tmpmodule->dir_output)) { // modulepart not supported
3691 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3692 exit;
3693 }
3694 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3695 $accessallowed = 1;
3696 }
3697 $original_file = $conf->$tmpmodule->dir_output.'/'.$fuser->id.'/'.$original_file;
3698 } elseif (preg_match('/^massfilesarea_([a-z]+)$/i', $modulepart, $reg)) {
3699 $tmpmodule = $reg[1];
3700 if (empty($conf->$tmpmodule->dir_output)) { // modulepart not supported
3701 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3702 exit;
3703 }
3704
3705 // Check fuser->rights->modulepart->myobject->read and fuser->rights->modulepart->read
3706 $partsofdirinoriginalfile = explode('/', $original_file);
3707 if (!empty($partsofdirinoriginalfile[1])) { // If original_file is xxx/filename (xxx is a part we will use)
3708 $partofdirinoriginalfile = $partsofdirinoriginalfile[0];
3709 if (($partofdirinoriginalfile && $fuser->hasRight($tmpmodule, $partofdirinoriginalfile, 'read')) || preg_match('/^specimen/i', $original_file)) {
3710 $accessallowed = 1;
3711 }
3712 }
3713 if ($fuser->hasRight($tmpmodule, $read) || preg_match('/^specimen/i', $original_file)) {
3714 $accessallowed = 1;
3715 }
3716 $original_file = $conf->$tmpmodule->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3717 } else {
3718 if (empty($conf->$modulepart->dir_output)) { // modulepart not supported
3719 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.');
3720 exit;
3721 }
3722
3723 // Check fuser->hasRight('modulepart', 'myobject', 'read') and fuser->hasRight('modulepart', 'read')
3724 $partsofdirinoriginalfile = explode('/', $original_file);
3725 if (!empty($partsofdirinoriginalfile[1])) { // If original_file is xxx/filename (xxx is a part we will use)
3726 $partofdirinoriginalfile = $partsofdirinoriginalfile[0];
3727 if ($partofdirinoriginalfile && ($fuser->hasRight($modulepart, $partofdirinoriginalfile, 'lire') || $fuser->hasRight($modulepart, $partofdirinoriginalfile, 'read'))) {
3728 $accessallowed = 1;
3729 }
3730 }
3731 if (($fuser->hasRight($modulepart, $lire) || $fuser->hasRight($modulepart, $read)) || ($fuser->hasRight($modulepart, 'all', $lire) || $fuser->hasRight($modulepart, 'all', $read))) {
3732 $accessallowed = 1;
3733 }
3734
3735 if (is_array($conf->$modulepart->multidir_output) && !empty($conf->$modulepart->multidir_output[$entity])) {
3736 $original_file = $conf->$modulepart->multidir_output[$entity].'/'.$original_file;
3737 } else {
3738 $original_file = $conf->$modulepart->dir_output.'/'.$original_file;
3739 }
3740 }
3741
3742 $parameters = array(
3743 'modulepart' => $modulepart,
3744 'original_file' => $original_file,
3745 'entity' => $entity,
3746 'fuser' => $fuser,
3747 'refname' => '',
3748 'mode' => $mode
3749 );
3750 $reshook = $hookmanager->executeHooks('checkSecureAccess', $parameters, $object);
3751 if ($reshook > 0) {
3752 if (!empty($hookmanager->resArray['original_file'])) {
3753 $original_file = $hookmanager->resArray['original_file'];
3754 }
3755 if (!empty($hookmanager->resArray['accessallowed'])) {
3756 $accessallowed = $hookmanager->resArray['accessallowed'];
3757 }
3758 if (!empty($hookmanager->resArray['sqlprotectagainstexternals'])) {
3759 $sqlprotectagainstexternals = $hookmanager->resArray['sqlprotectagainstexternals'];
3760 }
3761 }
3762 }
3763
3764 $ret = array(
3765 'accessallowed' => ($accessallowed ? 1 : 0),
3766 'sqlprotectagainstexternals' => $sqlprotectagainstexternals,
3767 'original_file' => $original_file
3768 );
3769
3770 return $ret;
3771}
3772
3781function dol_filecache($directory, $filename, $object)
3782{
3783 if (!dol_is_dir($directory)) {
3784 $result = dol_mkdir($directory);
3785 if ($result < -1) {
3786 dol_syslog("Failed to create the cache directory ".$directory, LOG_WARNING);
3787 }
3788 }
3789 $cachefile = $directory.$filename;
3790
3791 file_put_contents($cachefile, serialize($object), LOCK_EX);
3792
3793 dolChmod($cachefile, '0644');
3794}
3795
3804function dol_cache_refresh($directory, $filename, $cachetime)
3805{
3806 $now = dol_now();
3807 $cachefile = $directory.$filename;
3808 $refresh = !file_exists($cachefile) || ($now - $cachetime) > dol_filemtime($cachefile);
3809 return $refresh;
3810}
3811
3819function dol_readcachefile($directory, $filename)
3820{
3821 $cachefile = $directory.$filename;
3822 $object = unserialize(file_get_contents($cachefile));
3823 return $object;
3824}
3825
3832function dirbasename($pathfile)
3833{
3834 return preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'\//', '', $pathfile);
3835}
3836
3837
3849function getFilesUpdated(&$file_list, SimpleXMLElement $dir, $path = '', $pathref = '', &$checksumconcat = array())
3850{
3851 global $conffile;
3852
3853 $exclude = 'install';
3854
3855 foreach ($dir->md5file as $file) { // $file is a simpleXMLElement
3856 $filename = $path.$file['name'];
3857 $file_list['insignature'][] = $filename;
3858 $expectedsize = (empty($file['size']) ? '' : $file['size']);
3859 $expectedmd5 = (string) $file;
3860
3861 if (!file_exists($pathref.'/'.$filename)) {
3862 $file_list['missing'][] = array('filename' => $filename, 'expectedmd5' => $expectedmd5, 'expectedsize' => $expectedsize);
3863 } else {
3864 $md5_local = md5_file($pathref.'/'.$filename);
3865
3866 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
3867 $checksumconcat[] = $expectedmd5;
3868 } else {
3869 if ($md5_local != $expectedmd5) {
3870 $file_list['updated'][] = array('filename' => $filename, 'expectedmd5' => $expectedmd5, 'expectedsize' => $expectedsize, 'md5' => (string) $md5_local);
3871 }
3872 $checksumconcat[] = $md5_local;
3873 }
3874 }
3875 }
3876
3877 foreach ($dir->dir as $subdir) { // $subdir['name'] is '' or '/accountancy/admin' for example
3878 getFilesUpdated($file_list, $subdir, $path.$subdir['name'].'/', $pathref, $checksumconcat);
3879 }
3880
3881 return $file_list;
3882}
3883
3891function dragAndDropFileUpload($htmlname)
3892{
3893 global $object, $langs;
3894
3895 $out = "";
3896 $out .= '<div id="'.$htmlname.'Message" class="dragDropAreaMessage hidden"><span>'.img_picto("", 'download').'<br>'.$langs->trans("DropFileToAddItToObject").'</span></div>';
3897 $out .= "\n<!-- JS CODE TO ENABLE DRAG AND DROP OF FILE -->\n";
3898 $out .= "<script>";
3899 $out .= '
3900 jQuery(document).ready(function() {
3901 var enterTargetDragDrop = null;
3902
3903 $("#'.$htmlname.'").addClass("cssDragDropArea");
3904
3905 $(".cssDragDropArea").on("dragenter", function(ev, ui) {
3906 var dataTransfer = ev.originalEvent.dataTransfer;
3907 var dataTypes = dataTransfer.types;
3908 //console.log(dataTransfer);
3909 //console.log(dataTypes);
3910
3911 if (!dataTypes || ($.inArray(\'Files\', dataTypes) === -1)) {
3912 // The element dragged is not a file, so we avoid the "dragenter"
3913 ev.preventDefault();
3914 return false;
3915 }
3916
3917 // Entering drop area. Highlight area
3918 console.log("dragAndDropFileUpload: We add class highlightDragDropArea")
3919 enterTargetDragDrop = ev.target;
3920 $(this).addClass("highlightDragDropArea");
3921 $("#'.$htmlname.'Message").removeClass("hidden");
3922 ev.preventDefault();
3923 });
3924
3925 $(".cssDragDropArea").on("dragleave", function(ev) {
3926 // Going out of drop area. Remove Highlight
3927 if (enterTargetDragDrop == ev.target){
3928 console.log("dragAndDropFileUpload: We remove class highlightDragDropArea")
3929 $("#'.$htmlname.'Message").addClass("hidden");
3930 $(this).removeClass("highlightDragDropArea");
3931 }
3932 });
3933
3934 $(".cssDragDropArea").on("dragover", function(ev) {
3935 ev.preventDefault();
3936 return false;
3937 });
3938
3939 $(".cssDragDropArea").on("drop", function(e) {
3940 console.log("Trigger event file dropped. fk_element='.dol_escape_js((string) $object->id).' element='.dol_escape_js($object->element).'");
3941 e.preventDefault();
3942 fd = new FormData();
3943 fd.append("fk_element", "'.dol_escape_js((string) $object->id).'");
3944 fd.append("element", "'.dol_escape_js($object->element).'");
3945 fd.append("token", "'.currentToken().'");
3946 fd.append("action", "linkit");
3947
3948 var dataTransfer = e.originalEvent.dataTransfer;
3949
3950 if (dataTransfer.files && dataTransfer.files.length){
3951 var droppedFiles = e.originalEvent.dataTransfer.files;
3952 $.each(droppedFiles, function(index,file){
3953 fd.append("files[]", file,file.name)
3954 });
3955 }
3956 $(".cssDragDropArea").removeClass("highlightDragDropArea");
3957 counterdragdrop = 0;
3958 $.ajax({
3959 url: "'.DOL_URL_ROOT.'/core/ajax/fileupload.php",
3960 type: "POST",
3961 processData: false,
3962 contentType: false,
3963 data: fd,
3964 success:function() {
3965 console.log("Uploaded.", arguments);
3966 /* arguments[0] is the json string of files */
3967 /* arguments[1] is the value for variable "success", can be 0 or 1 */
3968 let listoffiles = JSON.parse(arguments[0]);
3969 console.log(listoffiles);
3970 let nboferror = 0;
3971 for (let i = 0; i < listoffiles.length; i++) {
3972 console.log(listoffiles[i].error);
3973 if (listoffiles[i].error) {
3974 nboferror++;
3975 }
3976 }
3977 console.log(nboferror);
3978 if (nboferror > 0) {
3979 window.location.href = "'.$_SERVER["PHP_SELF"].'?id='.dol_escape_js((string) $object->id).'&seteventmessages=ErrorOnAtLeastOneFileUpload:warnings";
3980 } else {
3981 window.location.href = "'.$_SERVER["PHP_SELF"].'?id='.dol_escape_js((string) $object->id).'&seteventmessages=UploadFileDragDropSuccess:mesgs";
3982 }
3983 },
3984 error:function() {
3985 console.log("Error Uploading.", arguments)
3986 if (arguments[0].status == 403) {
3987 window.location.href = "'.$_SERVER["PHP_SELF"].'?id='.dol_escape_js((string) $object->id).'&seteventmessages=ErrorUploadPermissionDenied:errors";
3988 }
3989 window.location.href = "'.$_SERVER["PHP_SELF"].'?id='.dol_escape_js((string) $object->id).'&seteventmessages=ErrorUploadFileDragDropPermissionDenied:errors";
3990 },
3991 })
3992 });
3993 });
3994 ';
3995 $out .= "</script>\n";
3996 return $out;
3997}
3998
4009function archiveOrBackupFile($srcfile, $max_versions = 5, $archivedir = '', $suffix = "v", $moveorcopy = 'move')
4010{
4011 $base_file_pattern = ($archivedir ? $archivedir : dirname($srcfile)).'/'.basename($srcfile).".".$suffix;
4012 $files_in_directory = glob($base_file_pattern . "*");
4013
4014 // Extract the modification timestamps for each file
4015 $files_with_timestamps = [];
4016 foreach ($files_in_directory as $file) {
4017 $files_with_timestamps[] = [
4018 'file' => $file,
4019 'timestamp' => filemtime($file)
4020 ];
4021 }
4022
4023 // Sort the files by modification date
4024 $sorted_files = [];
4025 while (count($files_with_timestamps) > 0) {
4026 $latest_file = null;
4027 $latest_index = null;
4028
4029 // Find the latest file by timestamp
4030 foreach ($files_with_timestamps as $index => $file_info) {
4031 if ($latest_file === null || (is_array($latest_file) && $file_info['timestamp'] > $latest_file['timestamp'])) {
4032 $latest_file = $file_info;
4033 $latest_index = $index;
4034 }
4035 }
4036
4037 // Add the latest file to the sorted list and remove it from the original list
4038 if ($latest_file !== null) {
4039 $sorted_files[] = $latest_file['file'];
4040 unset($files_with_timestamps[$latest_index]);
4041 }
4042 }
4043
4044 // Delete the oldest files to keep only the allowed number of versions
4045 if (count($sorted_files) >= $max_versions) {
4046 $oldest_files = array_slice($sorted_files, $max_versions - 1);
4047 foreach ($oldest_files as $oldest_file) {
4048 dol_delete_file($oldest_file, 0, 0, 0, null, false, 0);
4049 }
4050 }
4051
4052 $timestamp = dol_now('gmt');
4053 $new_backup = $srcfile . ".v" . $timestamp;
4054
4055 // Move or copy the original file to the new backup with the timestamp
4056 if ($moveorcopy == 'move') {
4057 $result = dol_move($srcfile, $new_backup, '0', 1, 0, 0);
4058 } else {
4059 $result = dol_copy($srcfile, $new_backup, '0', 1, 0, 0);
4060 }
4061
4062 if (!$result) {
4063 return false;
4064 }
4065
4066 return true;
4067}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
Class to manage agenda events (actions)
Class to scan for virus.
Class to manage ECM files.
Class to manage Trips and Expenses.
Class permettant la generation du formulaire html d'envoi de mail unitaire Usage: $formail = new Form...
Class of the module paid holiday.
Class to manage hooks.
Class to manage projects.
Class to manage tasks.
Class to manage Dolibarr users.
Class to manage utility methods.
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
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.
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:38
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_move($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array(), $entity=0)
Move a file into another name.
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)
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 an image file or a PDF into another image format.
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.
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:63
dol_readcachefile($directory, $filename)
Read object from cachefile.
dol_most_recent_file($dir, $regexfilter='', $excludefilter=array('(\.meta|_preview.*\.png) $', '^\.'), $nohook=0, $mode=0)
Return file(s) into a directory (by default most recent)
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.
getExecutableContent()
Return array of extension for executable files of text files that can contains executable code.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
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'.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
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_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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...
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
isAFileWithExecutableContent($filename)
Return if a file can contains executable content.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
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_sanitizePathName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a path name.
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.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
getRandomPassword($generic=false, $replaceambiguouschars=null, $length=32)
Return a generated password using default module.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
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.