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