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