dolibarr 24.0.2
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['agenda_id'])) {
1289 $ecmfile->agenda_id = $moreinfo['agenda_id'];
1290 }
1291 if (!empty($moreinfo) && !empty($moreinfo['position'])) {
1292 $ecmfile->position = $moreinfo['position'];
1293 }
1294 if (!empty($moreinfo) && !empty($moreinfo['cover'])) {
1295 $ecmfile->cover = $moreinfo['cover'];
1296 }
1297 if (!empty($moreinfo) && !empty($moreinfo['share'])) {
1298 $ecmfile->share = $moreinfo['share'];
1299 }
1300 if (! empty($entity)) {
1301 $ecmfile->entity = $entity;
1302 }
1303
1304 $resultecm = $ecmfile->create($user);
1305 if ($resultecm < 0) {
1306 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1307 } else {
1308 if (!empty($moreinfo) && !empty($moreinfo['array_options']) && is_array($moreinfo['array_options'])) {
1309 $ecmfile->array_options = $moreinfo['array_options'];
1310 $resultecm = $ecmfile->insertExtraFields();
1311 if ($resultecm < 0) {
1312 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1313 }
1314 }
1315 }
1316 } elseif ($resultecm < 0) {
1317 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1318 }
1319
1320 if ($resultecm > 0) {
1321 $result = true;
1322 } else {
1323 $result = false;
1324 }
1325 }
1326 }
1327
1328 if (empty($newmask)) {
1329 $newmask = getDolGlobalString('MAIN_UMASK', '0755');
1330 }
1331
1332 // Currently method is restricted to files (dol_delete_files previously used is for files, and mask usage if for files too)
1333 // to allow mask usage for dir, we should introduce a new param "isdir" to 1 to complete newmask like this
1334 // if ($isdir) $newmaskdec |= octdec('0111'); // Set x bit required for directories
1335 dolChmod($newpathofdestfile, $newmask);
1336 }
1337
1338 return $result;
1339}
1340
1351function dol_move_dir($srcdir, $destdir, $overwriteifexists = 1, $indexdatabase = 1, $renamedircontent = 1)
1352{
1353 $result = false;
1354
1355 dol_syslog("files.lib.php::dol_move_dir srcdir=".$srcdir." destdir=".$destdir." overwritifexists=".$overwriteifexists." indexdatabase=".$indexdatabase." renamedircontent=".$renamedircontent);
1356 $srcexists = dol_is_dir($srcdir);
1357 $srcbasename = basename($srcdir);
1358 $destexists = dol_is_dir($destdir);
1359
1360 if (!$srcexists) {
1361 dol_syslog("files.lib.php::dol_move_dir srcdir does not exists. Move fails");
1362 return false;
1363 }
1364
1365 if ($overwriteifexists || !$destexists) {
1366 $newpathofsrcdir = dol_osencode($srcdir);
1367 $newpathofdestdir = dol_osencode($destdir);
1368
1369 // On windows, if destination directory exists and is empty, command fails. So if overwrite is on, we first remove destination directory.
1370 // On linux, if destination directory exists and is empty, command succeed. So no need to delete di destination directory first.
1371 // Note: If dir exists and is not empty, it will and must fail on both linux and windows even, if option $overwriteifexists is on.
1372 if ($overwriteifexists) {
1373 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1374 if (is_dir($newpathofdestdir)) {
1375 @rmdir($newpathofdestdir);
1376 }
1377 }
1378 }
1379
1380 $result = @rename($newpathofsrcdir, $newpathofdestdir);
1381
1382 // Now rename contents in the directory after the move to match the new destination
1383 if ($result && $renamedircontent) {
1384 if (file_exists($newpathofdestdir)) {
1385 $destbasename = basename($newpathofdestdir);
1386 $files = dol_dir_list($newpathofdestdir);
1387 if (!empty($files) && is_array($files)) {
1388 foreach ($files as $key => $file) {
1389 if (!file_exists($file["fullname"])) {
1390 continue;
1391 }
1392 $filepath = $file["path"];
1393 $oldname = $file["name"];
1394
1395 $newname = str_replace($srcbasename, $destbasename, $oldname);
1396 if (!empty($newname) && $newname !== $oldname) {
1397 if ($file["type"] == "dir") {
1398 $res = dol_move_dir($filepath.'/'.$oldname, $filepath.'/'.$newname, $overwriteifexists, $indexdatabase, $renamedircontent);
1399 } else {
1400 $moreinfo = array('gen_or_uploaded' => 'unknown');
1401 $res = dol_move($filepath.'/'.$oldname, $filepath.'/'.$newname, '0', $overwriteifexists, 0, $indexdatabase, $moreinfo);
1402 }
1403 if (!$res) {
1404 return $result;
1405 }
1406 }
1407 }
1408 $result = true;
1409 }
1410 }
1411 }
1412 }
1413 return $result;
1414}
1415
1423function dol_unescapefile($filename)
1424{
1425 // Remove path information and dots around the filename, to prevent uploading
1426 // into different directories or replacing hidden system files.
1427 // Also remove control characters and spaces (\x00..\x20) around the filename:
1428 return trim(basename($filename), ".\x00..\x20");
1429}
1430
1431
1439function dolCheckVirus($src_file, $dest_file = '')
1440{
1441 global $db;
1442
1443 $reterrors = dolCheckOnFileName($src_file, $dest_file);
1444 if (!empty($reterrors)) {
1445 return $reterrors;
1446 }
1447
1448 if (getDolGlobalString('MAIN_ANTIVIRUS_UPLOAD_ON')) {
1449 if (!class_exists('AntiVir')) {
1450 require_once DOL_DOCUMENT_ROOT.'/core/class/antivir.class.php';
1451 }
1452 $antivir = new AntiVir($db);
1453 $result = $antivir->dol_avscan_file($src_file);
1454 if ($result < 0) { // If virus or error, we stop here
1455 $reterrors = $antivir->errors;
1456 return $reterrors;
1457 }
1458 }
1459 return array();
1460}
1461
1469function dolCheckOnFileName($src_file, $dest_file = '')
1470{
1471 if (preg_match('/\.pdf$/i', $dest_file)) {
1472 if (!getDolGlobalString('MAIN_ANTIVIRUS_ALLOW_JS_IN_PDF')) {
1473 dol_syslog("dolCheckOnFileName Check that pdf does not contains js code");
1474
1475 $tmp = file_get_contents(trim($src_file));
1476 if (preg_match('/[\n\s]+\/JavaScript[\n\s]+/m', $tmp)) {
1477 return array('File is a PDF with javascript inside');
1478 }
1479 } else {
1480 dol_syslog("dolCheckOnFileName Check js into pdf disabled");
1481 }
1482 }
1483
1484 return array();
1485}
1486
1487
1509function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan = 0, $uploaderrorcode = 0, $nohook = 0, $keyforsourcefile = 'addedfile', $upload_dir = '', $mode = 0)
1510{
1511 global $conf;
1512 global $object, $hookmanager;
1513
1514 $reshook = 0;
1515 $file_name = $dest_file;
1516 $successcode = 1;
1517
1518 if (empty($nohook)) {
1519 $reshook = $hookmanager->initHooks(array('fileslib'));
1520
1521 $parameters = array('dest_file' => $dest_file, 'src_file' => $src_file, 'file_name' => $file_name, 'varfiles' => $keyforsourcefile, 'allowoverwrite' => $allowoverwrite);
1522 $reshook = $hookmanager->executeHooks('moveUploadedFile', $parameters, $object);
1523 }
1524
1525 if (empty($reshook)) {
1526 // If an upload error has been reported
1527 if ($uploaderrorcode) {
1528 switch ($uploaderrorcode) {
1529 case UPLOAD_ERR_INI_SIZE: // 1
1530 return 'ErrorFileSizeTooLarge';
1531 case UPLOAD_ERR_FORM_SIZE: // 2 - Exceed the MAX_FILE_SIZE specified into a field in form
1532 return 'ErrorFileSizeTooLarge';
1533 case UPLOAD_ERR_PARTIAL: // 3
1534 return 'ErrorPartialFile';
1535 case UPLOAD_ERR_NO_TMP_DIR: //
1536 return 'ErrorNoTmpDir';
1537 case UPLOAD_ERR_CANT_WRITE:
1538 return 'ErrorFailedToWriteInDir';
1539 case UPLOAD_ERR_EXTENSION:
1540 return 'ErrorUploadBlockedByAddon';
1541 default:
1542 break;
1543 }
1544 }
1545
1546 // Security:
1547 // If we need to make a virus scan
1548 if (empty($disablevirusscan) && file_exists($src_file)) {
1549 $checkvirusarray = dolCheckVirus($src_file, $dest_file);
1550 if (count($checkvirusarray)) {
1551 dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$dest_file.'") KO with antivirus: errors='.implode(',', $checkvirusarray), LOG_WARNING);
1552 return 'ErrorFileIsInfectedWithAVirus: '.implode(',', $checkvirusarray);
1553 }
1554 }
1555
1556 // Security:
1557 // Disallow file with some extensions. We rename them.
1558 // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code.
1559 if (isAFileWithExecutableContent($dest_file) && !getDolGlobalString('MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED')) {
1560 // $upload_dir ends with a slash, so be must be sure the medias dir to compare to ends with slash too.
1561 $publicmediasdirwithslash = $conf->medias->multidir_output[$conf->entity];
1562 if (!preg_match('/\/$/', $publicmediasdirwithslash)) {
1563 $publicmediasdirwithslash .= '/';
1564 }
1565
1566 if (strpos($upload_dir, $publicmediasdirwithslash) !== 0 || !getDolGlobalInt("MAIN_DOCUMENT_DISABLE_NOEXE_IN_MEDIAS_DIR")) { // We never add .noexe on files into media directory
1567 $file_name .= '.noexe';
1568 $successcode = 2;
1569 }
1570 }
1571
1572 // Security:
1573 // We refuse cache files/dirs, upload using .. and pipes into filenames.
1574 if (preg_match('/^\./', basename($src_file)) || preg_match('/\.\./', $src_file) || preg_match('/[<>|]/', $src_file)) {
1575 dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING);
1576 return -1;
1577 }
1578
1579 // Security:
1580 // We refuse cache files/dirs, upload using .. and pipes into filenames.
1581 if (preg_match('/^\./', basename($dest_file)) || preg_match('/\.\./', $dest_file) || preg_match('/[<>|]/', $dest_file)) {
1582 dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING);
1583 return -2;
1584 }
1585 }
1586
1587 if ($reshook < 0) { // At least one blocking error returned by one hook
1588 $errmsg = implode(',', $hookmanager->errors);
1589 if (empty($errmsg)) {
1590 $errmsg = 'ErrorReturnedBySomeHooks'; // Should not occurs. Added if hook is bugged and does not set ->errors when there is error.
1591 }
1592 return $errmsg;
1593 } elseif (empty($reshook)) {
1594 // The file functions must be in OS filesystem encoding.
1595 $src_file_osencoded = dol_osencode($src_file);
1596 $file_name_osencoded = dol_osencode($file_name);
1597
1598 // Check if destination dir is writable
1599 if (!is_writable(dirname($file_name_osencoded))) {
1600 dol_syslog("Files.lib::dol_move_uploaded_file Dir ".dirname($file_name_osencoded)." is not writable. Return 'ErrorDirNotWritable'", LOG_WARNING);
1601 return 'ErrorDirNotWritable';
1602 }
1603
1604 // Check if destination file already exists
1605 if (!$allowoverwrite) {
1606 if (file_exists($file_name_osencoded)) {
1607 dol_syslog("Files.lib::dol_move_uploaded_file File ".$file_name." already exists. Return 'ErrorFileAlreadyExists'", LOG_WARNING);
1608 return 'ErrorFileAlreadyExists';
1609 }
1610 } else { // We are allowed to erase
1611 if (is_dir($file_name_osencoded)) { // If there is a directory with name of file to create
1612 dol_syslog("Files.lib::dol_move_uploaded_file A directory with name ".$file_name." already exists. Return 'ErrorDirWithFileNameAlreadyExists'", LOG_WARNING);
1613 return 'ErrorDirWithFileNameAlreadyExists';
1614 }
1615 }
1616
1617 // Move file using a simple system function
1618 if ($mode == 0) {
1619 $return = move_uploaded_file($src_file_osencoded, $file_name_osencoded);
1620 } else {
1621 $return = rename($src_file_osencoded, $file_name_osencoded);
1622 }
1623
1624 if ($return) {
1625 dolChmod($file_name_osencoded);
1626 dol_syslog("Files.lib::dol_move_uploaded_file Success to move ".$src_file." to ".$file_name." - Umask=" . getDolGlobalString('MAIN_UMASK'), LOG_DEBUG);
1627 return $successcode; // Success
1628 } else {
1629 dol_syslog("Files.lib::dol_move_uploaded_file Failed to move ".$src_file." to ".$file_name, LOG_ERR);
1630 return -3; // Unknown error
1631 }
1632 }
1633
1634 return $successcode; // Success
1635}
1636
1652function dol_delete_file($file, $disableglob = 0, $nophperrors = 0, $nohook = 0, $object = null, $allowdotdot = false, $indexdatabase = 1, $nolog = 0)
1653{
1654 global $db, $user;
1655 global $hookmanager;
1656
1657 if (empty($nolog)) {
1658 dol_syslog("dol_delete_file file=".$file." disableglob=".$disableglob." nophperrors=".$nophperrors." nohook=".$nohook);
1659 }
1660
1661 // Security:
1662 // We refuse transversal using .. and pipes into filenames.
1663 if ((!$allowdotdot && preg_match('/\.\./', $file)) || preg_match('/[<>|]/', $file)) {
1664 dol_syslog("Refused to delete file ".$file, LOG_WARNING);
1665 return false;
1666 }
1667
1668 $reshook = 0;
1669 if (empty($nohook) && !empty($hookmanager)) {
1670 $hookmanager->initHooks(array('fileslib'));
1671
1672 $parameters = array(
1673 'file' => $file,
1674 'disableglob' => $disableglob,
1675 'nophperrors' => $nophperrors
1676 );
1677 $reshook = $hookmanager->executeHooks('deleteFile', $parameters, $object);
1678 }
1679
1680 if (empty($nohook) && $reshook != 0) { // reshook = 0 to do standard actions, 1 = ok and replace, -1 = ko
1681 dol_syslog("reshook=".$reshook);
1682 if ($reshook < 0) {
1683 return false;
1684 }
1685 return true;
1686 } else {
1687 $file_osencoded = dol_osencode($file); // New filename encoded in OS filesystem encoding charset
1688 if (empty($disableglob) && !empty($file_osencoded)) {
1689 $ok = true;
1690 $globencoded = str_replace('[', '\[', $file_osencoded);
1691 $globencoded = str_replace(']', '\]', $globencoded);
1692 $listoffiles = glob($globencoded); // This scan dir for files. If file does not exists, return empty.
1693
1694 if (!empty($listoffiles) && is_array($listoffiles)) {
1695 foreach ($listoffiles as $filename) {
1696 if ($nophperrors) {
1697 $ok = @unlink($filename);
1698 } else {
1699 $ok = unlink($filename);
1700 }
1701
1702 // If it fails and it is because of the missing write permission on parent dir
1703 if (!$ok && file_exists(dirname($filename)) && !(fileperms(dirname($filename)) & 0200)) {
1704 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);
1705 dolChmod(dirname($filename), decoct(fileperms(dirname($filename)) | 0200));
1706 // Now we retry deletion
1707 if ($nophperrors) {
1708 $ok = @unlink($filename);
1709 } else {
1710 $ok = unlink($filename);
1711 }
1712 }
1713
1714 if ($ok) {
1715 if (empty($nolog)) {
1716 dol_syslog("Removed file ".$filename, LOG_DEBUG);
1717 }
1718
1719 // Delete entry into ecm database
1720 $rel_filetodelete = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filename);
1721 if (!preg_match('/(\/temp\/|\/thumbs\/|\.meta$)/', $rel_filetodelete)) { // If not a tmp file
1722 if (is_object($db) && $indexdatabase) { // $db may not be defined when lib is in a context with define('NOREQUIREDB',1)
1723 $rel_filetodelete = preg_replace('/^[\\/]/', '', $rel_filetodelete);
1724 $rel_filetodelete = preg_replace('/\.noexe$/', '', $rel_filetodelete);
1725
1726 dol_syslog("Try to remove also entries in database for full relative path = ".$rel_filetodelete, LOG_DEBUG);
1727 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1728 $ecmfile = new EcmFiles($db);
1729 $entity = (isset($object->entity) ? $object->entity : null);
1730 $result = $ecmfile->fetch(0, '', $rel_filetodelete, '', '', '', 0, $entity);
1731 if ($result >= 0 && $ecmfile->id > 0) {
1732 $result = $ecmfile->delete($user);
1733 }
1734 if ($result < 0) {
1735 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1736 }
1737 }
1738 }
1739 } else {
1740 dol_syslog("Failed to remove file ".$filename, LOG_WARNING);
1741 // TODO Failure to remove can be because file was already removed or because of permission
1742 // If error because it does not exists, we should return true, and we should return false if this is a permission problem
1743 }
1744 }
1745 } else {
1746 $ok = true; // nothing to delete when glob is on must return ok
1747 dol_syslog("No files to delete found", LOG_DEBUG);
1748 }
1749 } else {
1750 $ok = false;
1751 if ($nophperrors) {
1752 $ok = @unlink($file_osencoded);
1753 } else {
1754 $ok = unlink($file_osencoded);
1755 }
1756
1757 $filename = $file_osencoded;
1758
1759 // If it fails and it is because of the missing write permission on parent dir
1760 if (!$ok && file_exists(dirname($filename)) && !(fileperms(dirname($filename)) & 0200)) {
1761 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);
1762 dolChmod(dirname($filename), decoct(fileperms(dirname($filename)) | 0200));
1763 // Now we retry deletion
1764 if ($nophperrors) {
1765 $ok = @unlink($filename);
1766 } else {
1767 $ok = unlink($filename);
1768 }
1769 }
1770
1771 if ($ok) {
1772 if (empty($nolog)) {
1773 dol_syslog("Removed file ".$filename, LOG_DEBUG);
1774 }
1775
1776 // Delete entry into ecm database
1777 $rel_filetodelete = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filename);
1778 if (!preg_match('/(\/temp\/|\/thumbs\/|\.meta$)/', $rel_filetodelete)) { // If not a tmp file
1779 if (is_object($db) && $indexdatabase) { // $db may not be defined when lib is in a context with define('NOREQUIREDB',1)
1780 $rel_filetodelete = preg_replace('/^[\\/]/', '', $rel_filetodelete);
1781 $rel_filetodelete = preg_replace('/\.noexe$/', '', $rel_filetodelete);
1782
1783 dol_syslog("Try to remove also entries in database for full relative path = ".$rel_filetodelete, LOG_DEBUG);
1784 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1785 $ecmfile = new EcmFiles($db);
1786 $entity = (isset($object->entity) ? $object->entity : null);
1787 $result = $ecmfile->fetch(0, '', $rel_filetodelete, '', '', '', 0, $entity);
1788 if ($result >= 0 && $ecmfile->id > 0) {
1789 $result = $ecmfile->delete($user);
1790 }
1791 if ($result < 0) {
1792 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1793 }
1794 }
1795 }
1796 } else {
1797 dol_syslog("Failed to remove file ".$filename, LOG_WARNING);
1798 }
1799 }
1800
1801 return $ok;
1802 }
1803}
1804
1814function dol_delete_dir($dir, $nophperrors = 0)
1815{
1816 // Security:
1817 // We refuse transversal using .. and pipes into filenames.
1818 if (preg_match('/\.\./', $dir) || preg_match('/[<>|]/', $dir)) {
1819 dol_syslog("Refused to delete dir ".$dir.' (contains invalid char sequence)', LOG_WARNING);
1820 return false;
1821 }
1822
1823 $dir_osencoded = dol_osencode($dir);
1824 return ($nophperrors ? @rmdir($dir_osencoded) : rmdir($dir_osencoded));
1825}
1826
1840function dol_delete_dir_recursive($dir, $count = 0, $nophperrors = 0, $onlysub = 0, &$countdeleted = 0, $indexdatabase = 1, $nolog = 0, $level = 0)
1841{
1842 if (empty($nolog) || empty($level)) {
1843 dol_syslog("functions.lib:dol_delete_dir_recursive ".$dir, LOG_DEBUG);
1844 }
1845 if ($level > 1000) {
1846 dol_syslog("functions.lib:dol_delete_dir_recursive too many depth", LOG_WARNING);
1847 }
1848
1849 if (dol_is_dir($dir)) {
1850 $dir_osencoded = dol_osencode($dir);
1851 if ($handle = opendir("$dir_osencoded")) {
1852 while (false !== ($item = readdir($handle))) {
1853 if (!utf8_check($item)) {
1854 $item = mb_convert_encoding($item, 'UTF-8', 'ISO-8859-1'); // should be useless
1855 }
1856
1857 if ($item != "." && $item != "..") {
1858 if (is_dir(dol_osencode("$dir/$item")) && !is_link(dol_osencode("$dir/$item"))) {
1859 $count = dol_delete_dir_recursive("$dir/$item", $count, $nophperrors, 0, $countdeleted, $indexdatabase, $nolog, ($level + 1));
1860 } else {
1861 dolChmod(dol_osencode("$dir/$item")); // Try to set permission to write on file
1862 $result = dol_delete_file("$dir/$item", 1, $nophperrors, 0, null, false, $indexdatabase, $nolog);
1863 $count++;
1864 if ($result) {
1865 $countdeleted++;
1866 }
1867 //else print 'Error on '.$item."\n";
1868 }
1869 }
1870 }
1871 closedir($handle);
1872
1873 // Delete also the main directory
1874 if (empty($onlysub)) {
1875 $result = dol_delete_dir($dir, $nophperrors);
1876 $count++;
1877 if ($result) {
1878 $countdeleted++;
1879 }
1880 //else print 'Error on '.$dir."\n";
1881 }
1882 }
1883 }
1884
1885 return $count;
1886}
1887
1888
1898{
1899 global $langs, $conf;
1900
1901 // Define parent dir of elements
1902 $element = $object->element;
1903
1904 if ($object->element == 'order_supplier') {
1905 $dir = $conf->fournisseur->commande->dir_output;
1906 } elseif ($object->element == 'invoice_supplier') {
1907 $dir = $conf->fournisseur->facture->dir_output;
1908 } elseif ($object->element == 'project') {
1909 $dir = $conf->project->dir_output;
1910 } elseif ($object->element == 'shipping') {
1911 $dir = $conf->expedition->dir_output.'/sending';
1912 } elseif ($object->element == 'delivery') {
1913 $dir = $conf->expedition->dir_output.'/receipt';
1914 } elseif ($object->element == 'fichinter') {
1915 $dir = $conf->ficheinter->dir_output;
1916 } else {
1917 $dir = empty($conf->$element->dir_output) ? '' : $conf->$element->dir_output;
1918 }
1919
1920 if (empty($dir)) {
1921 $object->error = $langs->trans('ErrorObjectNoSupportedByFunction');
1922 return 0;
1923 }
1924
1925 $refsan = dol_sanitizeFileName($object->ref);
1926 $dir = $dir."/".$refsan;
1927 $filepreviewnew = $dir."/".$refsan.".pdf_preview.png";
1928 $filepreviewnewbis = $dir."/".$refsan.".pdf_preview-0.png";
1929 $filepreviewold = $dir."/".$refsan.".pdf.png";
1930
1931 // For new preview files
1932 if (file_exists($filepreviewnew) && is_writable($filepreviewnew)) {
1933 if (!dol_delete_file($filepreviewnew, 1)) {
1934 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewnew);
1935 return 0;
1936 }
1937 }
1938 if (file_exists($filepreviewnewbis) && is_writable($filepreviewnewbis)) {
1939 if (!dol_delete_file($filepreviewnewbis, 1)) {
1940 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewnewbis);
1941 return 0;
1942 }
1943 }
1944 // For old preview files
1945 if (file_exists($filepreviewold) && is_writable($filepreviewold)) {
1946 if (!dol_delete_file($filepreviewold, 1)) {
1947 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewold);
1948 return 0;
1949 }
1950 } else {
1951 $multiple = $filepreviewold.".";
1952 for ($i = 0; $i < 20; $i++) {
1953 $preview = $multiple.$i;
1954
1955 if (file_exists($preview) && is_writable($preview)) {
1956 if (!dol_delete_file($preview, 1)) {
1957 $object->error = $langs->trans("ErrorFailedToOpenFile", $preview);
1958 return 0;
1959 }
1960 }
1961 }
1962 }
1963
1964 return 1;
1965}
1966
1976{
1977 global $conf;
1978
1979 // Create meta file
1980 if (!getDolGlobalString('MAIN_DOC_CREATE_METAFILE')) {
1981 return 0; // By default, no metafile.
1982 }
1983
1984 // Define parent dir of elements
1985 $element = $object->element;
1986
1987 if ($object->element == 'order_supplier') {
1988 $dir = $conf->fournisseur->dir_output.'/commande';
1989 } elseif ($object->element == 'invoice_supplier') {
1990 $dir = $conf->fournisseur->dir_output.'/facture';
1991 } elseif ($object->element == 'project') {
1992 $dir = $conf->project->dir_output;
1993 } elseif ($object->element == 'shipping') {
1994 $dir = $conf->expedition->dir_output.'/sending';
1995 } elseif ($object->element == 'delivery') {
1996 $dir = $conf->expedition->dir_output.'/receipt';
1997 } elseif ($object->element == 'fichinter') {
1998 $dir = $conf->ficheinter->dir_output;
1999 } else {
2000 $dir = empty($conf->$element->dir_output) ? '' : $conf->$element->dir_output;
2001 }
2002
2003 if ($dir) {
2004 $object->fetch_thirdparty();
2005
2006 $objectref = dol_sanitizeFileName((string) $object->ref);
2007 $dir = $dir."/".$objectref;
2008 $file = $dir."/".$objectref.".meta";
2009
2010 if (!is_dir($dir)) {
2011 dol_mkdir($dir);
2012 }
2013
2014 $meta = '';
2015 if (is_dir($dir)) {
2016 if (is_countable($object->lines) && count($object->lines) > 0) {
2017 $nblines = count($object->lines);
2018 } else {
2019 $nblines = 0;
2020 }
2021 $client = $object->thirdparty->name." ".$object->thirdparty->address." ".$object->thirdparty->zip." ".$object->thirdparty->town;
2022 $meta = "REFERENCE=\"".$object->ref."\"
2023 DATE=\"" . dol_print_date($object->date, '')."\"
2024 NB_ITEMS=\"" . $nblines."\"
2025 CLIENT=\"" . $client."\"
2026 AMOUNT_EXCL_TAX=\"" . $object->total_ht."\"
2027 AMOUNT=\"" . $object->total_ttc."\"\n";
2028
2029 for ($i = 0; $i < $nblines; $i++) {
2030 //Pour les articles
2031 $meta .= "ITEM_".$i."_QUANTITY=\"".$object->lines[$i]->qty."\"
2032 ITEM_" . $i."_AMOUNT_WO_TAX=\"".$object->lines[$i]->total_ht."\"
2033 ITEM_" . $i."_VAT=\"".$object->lines[$i]->tva_tx."\"
2034 ITEM_" . $i."_DESCRIPTION=\"".str_replace("\r\n", "", nl2br($object->lines[$i]->desc))."\"
2035 ";
2036 }
2037 }
2038
2039 $fp = fopen($file, "w");
2040 fwrite($fp, $meta);
2041 fclose($fp);
2042
2043 dolChmod($file);
2044
2045 return 1;
2046 } else {
2047 dol_syslog('FailedToDetectDirInDolMetaCreateFor'.$object->element, LOG_WARNING);
2048 }
2049
2050 return 0;
2051}
2052
2053
2054
2063function dol_init_file_process($pathtoscan = '', $trackid = '')
2064{
2065 $listofpaths = array();
2066 $listofnames = array();
2067 $listofmimes = array();
2068
2069 if ($pathtoscan) {
2070 $listoffiles = dol_dir_list($pathtoscan, 'files');
2071 foreach ($listoffiles as $key => $val) {
2072 $listofpaths[] = $val['fullname'];
2073 $listofnames[] = $val['name'];
2074 $listofmimes[] = dol_mimetype($val['name']);
2075 }
2076 }
2077 $keytoavoidconflict = empty($trackid) ? '' : '-'.$trackid;
2078 $_SESSION["listofpaths".$keytoavoidconflict] = implode(';', $listofpaths);
2079 $_SESSION["listofnames".$keytoavoidconflict] = implode(';', $listofnames);
2080 $_SESSION["listofmimes".$keytoavoidconflict] = implode(';', $listofmimes);
2081}
2082
2083
2104function dol_add_file_process($upload_dir, $allowoverwrite = 0, $updatesessionordb = 0, $keyforsourcefile = 'addedfile', $savingdocmask = '', $link = null, $trackid = '', $generatethumbs = 1, $object = null, $forceFullTextIndexation = '', $mode = 0)
2105{
2106 global $db, $user, $conf, $langs;
2107
2108 $res = 0;
2109
2110 // If mode 1, prepare environment to be compatible with mode 0
2111 if ($mode == 1) {
2112 $_FILES = array($keyforsourcefile => array());
2113 $_FILES[$keyforsourcefile]['tmp_name'] = $keyforsourcefile;
2114 $_FILES[$keyforsourcefile]['name'] = $keyforsourcefile;
2115 $mode = 0;
2116 }
2117
2118 if (!empty($_FILES[$keyforsourcefile])) { // For view $_FILES[$keyforsourcefile]['error']
2119 dol_syslog('dol_add_file_process varfiles = '.$keyforsourcefile.' upload_dir='.$upload_dir.' allowoverwrite='.$allowoverwrite.' updatesessionordb='.$updatesessionordb.' savingdocmask='.$savingdocmask, LOG_DEBUG);
2120 $maxfilesinform = getDolGlobalInt("MAIN_SECURITY_MAX_ATTACHMENT_ON_FORMS", 10);
2121 if (is_array($_FILES[$keyforsourcefile]["name"]) && count($_FILES[$keyforsourcefile]["name"]) > $maxfilesinform) {
2122 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2123 setEventMessages($langs->trans("ErrorTooMuchFileInForm", $maxfilesinform), null, "errors");
2124 return -1;
2125 }
2126
2127 $result = dol_mkdir($upload_dir);
2128 //var_dump($result);exit;
2129
2130 if ($result >= 0) {
2131 $TFile = $_FILES[$keyforsourcefile];
2132 // Convert value of $TFile
2133 if (!is_array($TFile['name'])) {
2134 foreach ($TFile as $key => &$val) {
2135 $val = array($val);
2136 }
2137 }
2138
2139 $nbfile = count($TFile['name']);
2140 $nbok = 0;
2141 for ($i = 0; $i < $nbfile; $i++) {
2142 if (empty($TFile['name'][$i])) {
2143 continue; // For example, when submitting a form with no file name
2144 }
2145
2146 // Define $destfull (path to file including filename) and $destfile (only filename)
2147 $destfile = trim($TFile['name'][$i]);
2148 $destfull = $upload_dir."/".$destfile;
2149 $destfilewithoutext = preg_replace('/\.[^\.]+$/', '', $destfile);
2150
2151 if ($savingdocmask && strpos($savingdocmask, $destfilewithoutext) !== 0) {
2152 $destfile = trim(preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask));
2153 $destfull = $upload_dir."/".$destfile;
2154 }
2155
2156 $filenameto = basename($destfile);
2157 if (preg_match('/^\./', $filenameto)) {
2158 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2159 setEventMessages($langs->trans("ErrorFilenameCantStartWithDot", $filenameto), null, 'errors');
2160 break;
2161 }
2162 // dol_sanitizeFileName the file name and lowercase extension
2163 $info = pathinfo($destfull);
2164 $destfull = $info['dirname'].'/'.dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : ''));
2165 $info = pathinfo($destfile);
2166 $destfile = dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : ''));
2167
2168 // Check extension is allowed for upload.
2169 $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';
2170 $fileextensionrestriction = getDolGlobalString("MAIN_FILE_EXTENSION_UPLOAD_RESTRICTION", $defaultexecutableextensions);
2171 if (!empty($fileextensionrestriction)) {
2172 $arrayofregexextension = explode(",", $fileextensionrestriction);
2173
2174 foreach ($arrayofregexextension as $fileextension) {
2175 if (preg_match('/\.'.preg_quote(trim($fileextension), '/').'$/i', $destfull)) {
2176 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2177 setEventMessages($langs->trans("ErrorFilenameExtensionNotAllowed", $filenameto), null, 'errors');
2178 return -1;
2179 }
2180 }
2181 }
2182
2183 // We apply dol_string_nohtmltag also to clean file names (this remove duplicate spaces) because
2184 // this function is also applied when we rename and when we make try to download file (by the GETPOST(filename, 'alphanohtml') call).
2185 $destfile = dol_string_nohtmltag($destfile);
2186 $destfull = dol_string_nohtmltag($destfull);
2187
2188 // Check that filename is not the one of a reserved allowed CLI command
2189 global $dolibarr_main_restrict_os_commands;
2190 if (!empty($dolibarr_main_restrict_os_commands)) {
2191 $arrayofallowedcommand = explode(',', $dolibarr_main_restrict_os_commands);
2192 $arrayofallowedcommand = array_map('trim', $arrayofallowedcommand);
2193 if (in_array($destfile, $arrayofallowedcommand)) {
2194 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now.
2195 setEventMessages($langs->trans("ErrorFilenameReserved", $destfile), null, 'errors');
2196 return -1;
2197 }
2198 }
2199
2200 // Move file from source directory to final destination. Check for virus is also embedded and a .noexe may also be appended on file name.
2201 $resupload = dol_move_uploaded_file($TFile['tmp_name'][$i], $destfull, $allowoverwrite, 0, $TFile['error'][$i], 0, $keyforsourcefile, $upload_dir, $mode);
2202
2203 if (is_numeric($resupload) && $resupload > 0) { // $resupload can be 'ErrorFileAlreadyExists', 'ErrorFileIsInfectedWithAVirus...'
2204 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2205
2206 $tmparraysize = getDefaultImageSizes();
2207 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
2208 $maxheightsmall = $tmparraysize['maxheightsmall'];
2209 $maxwidthmini = $tmparraysize['maxwidthmini'];
2210 $maxheightmini = $tmparraysize['maxheightmini'];
2211 //$quality = $tmparraysize['quality'];
2212 $quality = 50; // For thumbs, we force quality to 50
2213
2214 // Generate thumbs.
2215 if ($generatethumbs) {
2216 if (image_format_supported($destfull) == 1) {
2217 // Create thumbs
2218 // We can't use $object->addThumbs here because there is no $object known
2219
2220 // Used on logon for example
2221 $imgThumbSmall = vignette($destfull, $maxwidthsmall, $maxheightsmall, '_small', $quality, "thumbs");
2222 // Create mini thumbs for image (Ratio is near 16/9)
2223 // Used on menu or for setup page for example
2224 $imgThumbMini = vignette($destfull, $maxwidthmini, $maxheightmini, '_mini', $quality, "thumbs");
2225 }
2226 }
2227
2228 // Update session
2229 if (empty($updatesessionordb)) {
2230 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2231 $formmail = new FormMail($db);
2232 $formmail->trackid = $trackid;
2233 $formmail->add_attached_files($destfull, $destfile, $TFile['type'][$i]);
2234 }
2235
2236 // Update index table of files (llx_ecm_files)
2237 if ($updatesessionordb == 1) {
2238 $sharefile = 0;
2239 if ($TFile['type'][$i] == 'application/pdf' && strpos($_SERVER["REQUEST_URI"], 'product') !== false && getDolGlobalString('PRODUCT_ALLOW_EXTERNAL_DOWNLOAD')) {
2240 $sharefile = 1;
2241 }
2242
2243 // If we allow overwrite, we may need to also overwrite index, so we delete index first so insert can work
2244 if ($allowoverwrite) {
2245 deleteFilesIntoDatabaseIndex($upload_dir, basename($destfile).($resupload == 2 ? '.noexe' : ''), '', $object);
2246 }
2247
2248 $result = addFileIntoDatabaseIndex($upload_dir, basename($destfile).($resupload == 2 ? '.noexe' : ''), $TFile['name'][$i], 'uploaded', $sharefile, $object, $forceFullTextIndexation);
2249 if ($result < 0) {
2250 if ($allowoverwrite) {
2251 // Do not show error message. We can have an error due to DB_ERROR_RECORD_ALREADY_EXISTS
2252 } else {
2253 setEventMessages('WarningFailedToAddFileIntoDatabaseIndex', null, 'warnings');
2254 }
2255 }
2256 }
2257
2258 $nbok++;
2259 } else {
2260 $langs->load("errors");
2261 if (is_numeric($resupload) && $resupload < 0) { // Unknown error
2262 setEventMessages($langs->trans("ErrorFileNotUploaded"), null, 'errors');
2263 } elseif (preg_match('/ErrorFileIsInfectedWithAVirus/', $resupload)) { // Files infected by a virus
2264 if (preg_match('/File is a PDF with javascript inside/', $resupload)) {
2265 setEventMessages($langs->trans("ErrorFileIsAnInfectedPDFWithJSInside"), null, 'errors');
2266 } else {
2267 setEventMessages($langs->trans("ErrorFileIsInfectedWithAVirus").'<br>'.dolGetFirstLineOfText($resupload), null, 'errors');
2268 }
2269 } else { // Known error
2270 setEventMessages($langs->trans($resupload), null, 'errors');
2271 }
2272 }
2273 }
2274 if ($nbok > 0) {
2275 $res = $nbok;
2276 setEventMessages($langs->trans("FileTransferComplete"), null, 'mesgs');
2277 }
2278 } else {
2279 setEventMessages($langs->trans("ErrorFailedToCreateDir", $upload_dir), null, 'errors');
2280 }
2281 } elseif ($link) {
2282 require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php';
2283 $linkObject = new Link($db);
2284 $linkObject->entity = $conf->entity;
2285 $linkObject->url = $link;
2286 $linkObject->objecttype = GETPOST('objecttype', 'alpha');
2287 $linkObject->objectid = GETPOSTINT('objectid');
2288 $linkObject->label = GETPOST('label', 'alpha');
2289 $res = $linkObject->create($user);
2290
2291 if ($res > 0) {
2292 setEventMessages($langs->trans("LinkComplete"), null, 'mesgs');
2293 } else {
2294 setEventMessages($langs->trans("ErrorFileNotLinked"), null, 'errors');
2295 }
2296 } else {
2297 $langs->load("errors");
2298 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("File")), null, 'errors');
2299 }
2300
2301 return $res;
2302}
2303
2304
2316function dol_remove_file_process($filenb, $donotupdatesession = 0, $donotdeletefile = 1, $trackid = '')
2317{
2318 global $db, $langs;
2319
2320 $keytodelete = $filenb;
2321 $keytodelete--;
2322
2323 $listofpaths = array();
2324 $listofnames = array();
2325 $listofmimes = array();
2326 $keytoavoidconflict = empty($trackid) ? '' : '-'.$trackid;
2327 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
2328 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
2329 }
2330 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
2331 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
2332 }
2333 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
2334 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
2335 }
2336
2337 if ($keytodelete >= 0) {
2338 $pathtodelete = $listofpaths[$keytodelete];
2339 $filetodelete = $listofnames[$keytodelete];
2340 if (empty($donotdeletefile)) {
2341 $result = dol_delete_file($pathtodelete, 1); // The delete of ecm database is inside the function dol_delete_file
2342 } else {
2343 $result = 0;
2344 }
2345 if ($result >= 0) {
2346 if (empty($donotdeletefile)) {
2347 $langs->load("other");
2348 setEventMessages($langs->trans("FileWasRemoved", $filetodelete), null, 'mesgs');
2349 }
2350 if (empty($donotupdatesession)) {
2351 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2352 $formmail = new FormMail($db);
2353 $formmail->trackid = $trackid;
2354 $formmail->remove_attached_files($keytodelete);
2355 }
2356 }
2357 }
2358}
2359
2360
2375function addFileIntoDatabaseIndex($dir, $file, $fullpathorig = '', $mode = 'uploaded', $setsharekey = 0, $object = null, $forceFullTextIndexation = '')
2376{
2377 global $db, $user;
2378
2379 $result = 0;
2380 $error = 0;
2381
2382 dol_syslog("addFileIntoDatabaseIndex dir=".$dir." file=".$file, LOG_DEBUG);
2383
2384 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
2385
2386 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a temporary directory. TODO Does this test work ?
2387 $filename = basename(preg_replace('/\.noexe$/', '', $file));
2388 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
2389 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
2390
2391 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
2392 $ecmfile = new EcmFiles($db);
2393 $ecmfile->filepath = $rel_dir;
2394 $ecmfile->filename = $filename;
2395 $ecmfile->label = md5_file(dol_osencode($dir.'/'.$file)); // MD5 of file content
2396 $ecmfile->fullpath_orig = $fullpathorig;
2397 $ecmfile->gen_or_uploaded = $mode;
2398 $ecmfile->description = ''; // indexed content
2399 $ecmfile->keywords = ''; // keyword content
2400
2401 if (is_object($object) && $object->id > 0) {
2402 $ecmfile->src_object_id = $object->id;
2403 if (isset($object->table_element)) {
2404 $ecmfile->src_object_type = $object->table_element;
2405 } else {
2406 dol_syslog('Error: object ' . get_class($object) . ' has no table_element attribute.');
2407 return -1;
2408 }
2409 if (isset($object->src_object_description)) {
2410 $ecmfile->description = $object->src_object_description;
2411 }
2412 if (isset($object->src_object_keywords)) {
2413 $ecmfile->keywords = $object->src_object_keywords;
2414 }
2415 if (isset($object->entity)) {
2416 $ecmfile->entity = $object->entity;
2417 }
2418 }
2419
2420 if (getDolGlobalString('MAIN_FORCE_SHARING_ON_ANY_UPLOADED_FILE')) {
2421 $setsharekey = 1;
2422 }
2423
2424 if ($setsharekey) {
2425 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
2426 $ecmfile->share = getRandomPassword(true);
2427 }
2428
2429 // Use a convert tool for Doc to Text
2430 $useFullTextIndexation = getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT'); // Can be '', 'pdftotext' or 'docling'
2431 if (empty($useFullTextIndexation) && $forceFullTextIndexation == '1') {
2432 if (getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_PDFTOTEXT')) { // Command line for pdftotext
2433 $useFullTextIndexation = 'pdftotext';
2434 } elseif (getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_DOCLING')) { // Command line for docling
2435 $useFullTextIndexation = 'docling';
2436 }
2437 }
2438
2439 //$useFullTextIndexation = 1;
2440 if ($useFullTextIndexation) {
2441 $ecmfile->filepath = $rel_dir;
2442 $ecmfile->filename = $filename;
2443
2444 $filetoprocess = $dir.'/'.$ecmfile->filename;
2445
2446 $textforfulltextindex = '';
2447 $keywords = '';
2448 $cmd = '';
2449 if (preg_match('/\.pdf/i', $filename)) {
2450 // Convertfile into text
2451 $result = dolDocToText($filetoprocess);
2452
2453 if (empty($result['error'])) {
2454 $textforfulltextindex = $result['content'];
2455 $filetoprocess = $result['keywords'];
2456 $cmd = $result['cmd'];
2457 } else {
2458 $error++;
2459 }
2460 }
2461
2462 if ($cmd) {
2463 $ecmfile->description = 'File content generated by '.$cmd;
2464 }
2465 $ecmfile->content = $textforfulltextindex;
2466 $ecmfile->keywords = $keywords;
2467 }
2468
2469 if (!$error) {
2470 $result = $ecmfile->create($user);
2471 if ($result < 0) {
2472 dol_syslog($ecmfile->error);
2473 }
2474 }
2475 }
2476
2477 return $result;
2478}
2479
2489function deleteFilesIntoDatabaseIndex($dir, $file, $mode = 'uploaded', $object = null)
2490{
2491 global $conf, $db;
2492
2493 $error = 0;
2494
2495 if (empty($dir)) {
2496 dol_syslog("deleteFilesIntoDatabaseIndex: dir parameter can't be empty", LOG_ERR);
2497 return -1;
2498 }
2499
2500 dol_syslog("deleteFilesIntoDatabaseIndex dir=".$dir." file=".$file, LOG_DEBUG);
2501
2502 $db->begin();
2503
2504 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
2505
2506 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a temporary directory. TODO Does this test work ?
2507 //$filename = basename($file);
2508 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
2509 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
2510
2511 if (!$error) {
2512 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'ecm_files';
2513 if (isset($object->entity)) {
2514 $sql .= ' WHERE entity = ' . ((int) $object->entity);
2515 } else {
2516 $sql .= ' WHERE entity = ' . ((int) $conf->entity);
2517 }
2518 $sql .= " AND filepath = '".$db->escape($rel_dir)."'";
2519 if ($file) {
2520 $sql .= " AND filename = '".$db->escape($file)."'";
2521 }
2522 if ($mode) {
2523 $sql .= " AND gen_or_uploaded = '".$db->escape($mode)."'";
2524 }
2525
2526 $resql = $db->query($sql);
2527 if (!$resql) {
2528 $error++;
2529 dol_syslog(__FUNCTION__.' '.$db->lasterror(), LOG_ERR);
2530 }
2531 }
2532 }
2533
2534 // Commit or rollback
2535 if ($error) {
2536 $db->rollback();
2537 return -1 * $error;
2538 } else {
2539 $db->commit();
2540 return 1;
2541 }
2542}
2543
2551function isRealPdf(string $filePath)
2552{
2553 if (!is_file($filePath) || !is_readable($filePath)) {
2554 return false;
2555 }
2556
2557 // Open file
2558 $handle = fopen($filePath, 'rb');
2559 if (!$handle) {
2560 return false;
2561 }
2562 $header = fread($handle, 5);
2563 fclose($handle);
2564
2565 if ($header !== '%PDF-') {
2566 return false;
2567 }
2568
2569 // Check using finfo_file
2570 /*
2571 $finfo = finfo_open(FILEINFO_MIME_TYPE);
2572 $mime = finfo_file($finfo, $filePath);
2573 finfo_close($finfo);
2574 if ($mime !== 'application/pdf') {
2575 return false;
2576 }
2577 */
2578
2579 return true;
2580}
2581
2593function dol_convert_file($fileinput, $ext = 'png', $fileoutput = '', $page = '')
2594{
2595 if (class_exists('Imagick')) {
2596 $image = new Imagick();
2597 try {
2598 // 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
2599 // that file is really a PDF file.
2600 // Note: The Imagick policy options can be disabled into /etc/ImageMagick*/policy.xml.
2601 if (!isRealPdf($fileinput)) {
2602 dol_syslog("We try to convert a PDF file with name ".$fileinput." but it is not a real PDF file (hack attempt ?).", LOG_WARNING);
2603 return -4;
2604 }
2605
2606 $filetoconvert = $fileinput.(($page != '') ? '['.$page.']' : '');
2607 //var_dump($filetoconvert);
2608 $ret = $image->readImage($filetoconvert);
2609 } catch (Exception $e) {
2610 $ext = pathinfo($fileinput, PATHINFO_EXTENSION);
2611 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);
2612 return 0;
2613 }
2614
2615 if ($ret) {
2616 $ret = $image->setImageFormat($ext);
2617 if ($ret) {
2618 if (empty($fileoutput)) {
2619 $fileoutput = $fileinput.".".$ext;
2620 }
2621
2622 $count = $image->getNumberImages();
2623
2624 if (!dol_is_file($fileoutput) || is_writable($fileoutput)) {
2625 try {
2626 $ret = $image->writeImages($fileoutput, true);
2627 } catch (Exception $e) {
2628 dol_syslog($e->getMessage(), LOG_WARNING);
2629 }
2630 } else {
2631 dol_syslog("Warning: Failed to write cache preview file '.$fileoutput.'. Check permission on file/dir", LOG_ERR);
2632 }
2633 if ($ret) {
2634 return $count;
2635 } else {
2636 return -3;
2637 }
2638 } else {
2639 return -2;
2640 }
2641 } else {
2642 return -1;
2643 }
2644 } else {
2645 return 0;
2646 }
2647}
2648
2649
2661function dol_compress_file($inputfile, $outputfile, $mode = "gz", &$errorstring = null)
2662{
2663 $foundhandler = 0;
2664 //var_dump(basename($inputfile)); exit;
2665
2666 try {
2667 dol_syslog("dol_compress_file mode=".$mode." inputfile=".$inputfile." outputfile=".$outputfile);
2668
2669 $data = implode("", file(dol_osencode($inputfile)));
2670 $compressdata = null;
2671 if ($mode == 'gz' && function_exists('gzencode')) {
2672 $foundhandler = 1;
2673 $compressdata = gzencode($data, 9);
2674 } elseif ($mode == 'bz' && function_exists('bzcompress')) {
2675 $foundhandler = 1;
2676 $compressdata = bzcompress($data, 9);
2677 } elseif ($mode == 'zstd' && function_exists('zstd_compress')) {
2678 $foundhandler = 1;
2679 $compressdata = zstd_compress($data, 9);
2680 } elseif ($mode == 'zip') {
2681 if (class_exists('ZipArchive') && getDolGlobalString('MAIN_USE_ZIPARCHIVE_FOR_ZIP_COMPRESS')) {
2682 $foundhandler = 1;
2683
2684 $rootPath = realpath($inputfile);
2685
2686 dol_syslog("Class ZipArchive is set so we zip using ZipArchive to zip into ".$outputfile.' rootPath='.$rootPath);
2687 $zip = new ZipArchive();
2688
2689 if ($zip->open($outputfile, ZipArchive::CREATE) !== true) {
2690 $errorstring = "dol_compress_file failure - Failed to open file ".$outputfile."\n";
2691 dol_syslog($errorstring, LOG_ERR);
2692
2693 global $errormsg;
2694 $errormsg = $errorstring;
2695
2696 return -6;
2697 }
2698
2699 // Create recursive directory iterator
2701 $files = new RecursiveIteratorIterator(
2702 new RecursiveDirectoryIterator($rootPath, FilesystemIterator::UNIX_PATHS),
2703 RecursiveIteratorIterator::LEAVES_ONLY
2704 );
2705 '@phan-var-force SplFileInfo[] $files';
2706
2707 foreach ($files as $name => $file) {
2708 // Skip directories (they would be added automatically)
2709 if (!$file->isDir()) {
2710 // Get real and relative path for current file
2711 $filePath = $file->getPath(); // the full path with filename using the $inputdir root.
2712 $fileName = $file->getFilename();
2713 $fileFullRealPath = $file->getRealPath(); // the full path with name and transformed to use real path directory.
2714
2715 //$relativePath = substr($fileFullRealPath, strlen($rootPath) + 1);
2716 $relativePath = substr(($filePath ? $filePath.'/' : '').$fileName, strlen($rootPath) + 1);
2717
2718 // Add current file to archive
2719 $zip->addFile($fileFullRealPath, $relativePath);
2720 }
2721 }
2722
2723 // Zip archive will be created only after closing object
2724 $zip->close();
2725
2726 dol_syslog("dol_compress_file success - ".$zip->numFiles." files");
2727 return 1;
2728 }
2729
2730 if (defined('ODTPHP_PATHTOPCLZIP')) {
2731 $foundhandler = 1;
2732
2733 include_once ODTPHP_PATHTOPCLZIP.'pclzip.lib.php';
2734 $archive = new PclZip($outputfile);
2735
2736 $result = $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
2737
2738 if ($result === 0) {
2739 global $errormsg;
2740 $errormsg = $archive->errorInfo(true);
2741
2742 if ($archive->errorCode() == PCLZIP_ERR_WRITE_OPEN_FAIL) {
2743 $errorstring = "PCLZIP_ERR_WRITE_OPEN_FAIL";
2744 dol_syslog("dol_compress_file error - archive->errorCode() = PCLZIP_ERR_WRITE_OPEN_FAIL", LOG_ERR);
2745 return -4;
2746 }
2747
2748 $errorstring = "dol_compress_file error archive->errorCode = ".$archive->errorCode()." errormsg=".$errormsg;
2749 dol_syslog("dol_compress_file failure - ".$errormsg, LOG_ERR);
2750 return -3;
2751 } else {
2752 dol_syslog("dol_compress_file success - ".count($result)." files");
2753 return 1;
2754 }
2755 }
2756 }
2757
2758 if ($foundhandler && is_string($compressdata)) {
2759 $fp = fopen($outputfile, "w");
2760 fwrite($fp, $compressdata);
2761 fclose($fp);
2762 return 1;
2763 } else {
2764 $errorstring = "Try to zip with format ".$mode." with no handler for this format";
2765 dol_syslog($errorstring, LOG_ERR);
2766
2767 global $errormsg;
2768 $errormsg = $errorstring;
2769 return -2;
2770 }
2771 } catch (Exception $e) {
2772 global $langs, $errormsg;
2773 $langs->load("errors");
2774 $errormsg = $langs->trans("ErrorFailedToWriteInDir");
2775
2776 $errorstring = "Failed to open file ".$outputfile;
2777 dol_syslog($errorstring, LOG_ERR);
2778 return -1;
2779 }
2780}
2781
2790function dol_uncompress($inputfile, $outputdir)
2791{
2792 global $langs, $db;
2793
2794 $fileinfo = pathinfo($inputfile);
2795 $fileinfo["extension"] = strtolower($fileinfo["extension"]);
2796
2797 if ($fileinfo["extension"] == "zip") {
2798 if (defined('ODTPHP_PATHTOPCLZIP') && !getDolGlobalString('MAIN_USE_ZIPARCHIVE_FOR_ZIP_UNCOMPRESS')) {
2799 dol_syslog("Constant ODTPHP_PATHTOPCLZIP for pclzip library is set to ".ODTPHP_PATHTOPCLZIP.", so we use Pclzip to unzip into ".$outputdir);
2800 include_once ODTPHP_PATHTOPCLZIP.'pclzip.lib.php';
2801 $archive = new PclZip($inputfile);
2802
2803 // We create output dir manually, so it uses the correct permission (When created by the archive->extract, dir is rwx for everybody).
2804 dol_mkdir(dol_sanitizePathName($outputdir));
2805
2806 try {
2807 // Extract into outputdir, but only files that match the regex '/^((?!\.\.).)*$/' that means "does not include .."
2808 $result = $archive->extract(PCLZIP_OPT_PATH, $outputdir, PCLZIP_OPT_BY_PREG, '/^((?!\.\.).)*$/');
2809 } catch (Exception $e) {
2810 return array('error' => $e->getMessage());
2811 }
2812
2813 if (!is_array($result) && $result <= 0) {
2814 return array('error' => $archive->errorInfo(true));
2815 } else {
2816 $ok = 1;
2817 $errmsg = '';
2818 // Loop on each file to check result for unzipping file
2819 foreach ($result as $key => $val) {
2820 if ($val['status'] == 'path_creation_fail') {
2821 $langs->load("errors");
2822 $ok = 0;
2823 $errmsg = $langs->trans("ErrorFailToCreateDir", $val['filename']);
2824 break;
2825 }
2826 if ($val['status'] == 'write_protected') {
2827 $langs->load("errors");
2828 $ok = 0;
2829 $errmsg = $langs->trans("ErrorFailToCreateFile", $val['filename']);
2830 break;
2831 }
2832 }
2833
2834 if ($ok) {
2835 return array();
2836 } else {
2837 return array('error' => $errmsg);
2838 }
2839 }
2840 }
2841
2842 if (class_exists('ZipArchive')) { // Must install php-zip to have it
2843 dol_syslog("Class ZipArchive is set so we unzip using ZipArchive to unzip into ".$outputdir);
2844 $zip = new ZipArchive();
2845 $res = $zip->open($inputfile);
2846 if ($res === true) {
2847 //$zip->extractTo($outputdir.'/');
2848 // 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
2849 // python3 path_traversal_archiver.py <Created_file_name> test.zip -l 10 -p tmp/
2850 // with -l is the range of dot to go back in path.
2851 // and path_traversal_archiver.py found at https://github.com/Alamot/code-snippets/blob/master/path_traversal/path_traversal_archiver.py
2852 for ($i = 0; $i < $zip->numFiles; $i++) {
2853 if (preg_match('/\.\./', $zip->getNameIndex($i))) {
2854 dol_syslog("Warning: Try to unzip a file with a transversal path ".$zip->getNameIndex($i), LOG_WARNING);
2855 continue; // Discard the file
2856 }
2857 $zip->extractTo($outputdir.'/', array($zip->getNameIndex($i)));
2858 }
2859
2860 $zip->close();
2861 return array();
2862 } else {
2863 return array('error' => 'ErrUnzipFails');
2864 }
2865 }
2866
2867 return array('error' => 'ErrNoZipEngine');
2868 } elseif (in_array($fileinfo["extension"], array('gz', 'bz2', 'zst'))) {
2869 include_once DOL_DOCUMENT_ROOT."/core/class/utils.class.php";
2870 $utils = new Utils($db);
2871
2872 dol_mkdir(dol_sanitizePathName($outputdir));
2873 $outputfilename = escapeshellcmd(dol_sanitizePathName($outputdir).'/'.dol_sanitizeFileName($fileinfo["filename"]));
2874 dol_delete_file($outputfilename.'.tmp');
2875 dol_delete_file($outputfilename.'.err');
2876
2877 $extension = strtolower(pathinfo($fileinfo["filename"], PATHINFO_EXTENSION));
2878 if ($extension == "tar") {
2879 $cmd = 'tar -C '.escapeshellcmd(dol_sanitizePathName($outputdir)).' -xvf '.escapeshellcmd(dol_sanitizePathName($fileinfo["dirname"]).'/'.dol_sanitizeFileName($fileinfo["basename"]));
2880
2881 $resarray = $utils->executeCLI($cmd, $outputfilename.'.tmp', 0, $outputfilename.'.err', 0);
2882 if ($resarray["result"] != 0) {
2883 $resarray["error"] .= file_get_contents($outputfilename.'.err');
2884 }
2885 } else {
2886 $program = "";
2887 if ($fileinfo["extension"] == "gz") {
2888 $program = 'gzip';
2889 } elseif ($fileinfo["extension"] == "bz2") {
2890 $program = 'bzip2';
2891 } elseif ($fileinfo["extension"] == "zst") {
2892 $program = 'zstd';
2893 } else {
2894 return array('error' => 'ErrorBadFileExtension');
2895 }
2896 $cmd = $program.' -dc '.escapeshellcmd(dol_sanitizePathName($fileinfo["dirname"]).'/'.dol_sanitizeFileName($fileinfo["basename"]));
2897 $cmd .= ' > '.$outputfilename;
2898
2899 $resarray = $utils->executeCLI($cmd, $outputfilename.'.tmp', 0, null, 1, $outputfilename.'.err');
2900 if ($resarray["result"] != 0) {
2901 $errfilecontent = @file_get_contents($outputfilename.'.err');
2902 if ($errfilecontent) {
2903 $resarray["error"] .= " - ".$errfilecontent;
2904 }
2905 }
2906 }
2907 return $resarray["result"] != 0 ? array('error' => $resarray["error"]) : array();
2908 }
2909
2910 return array('error' => 'ErrorBadFileExtension');
2911}
2912
2913
2926function dol_compress_dir($inputdir, $outputfile, $mode = "zip", $excludefiles = '', $rootdirinzip = '', $newmask = '0')
2927{
2928 $foundhandler = 0;
2929
2930 dol_syslog("Try to zip dir ".$inputdir." into ".$outputfile." mode=".$mode);
2931
2932 if (!dol_is_dir(dirname($outputfile)) || !is_writable(dirname($outputfile))) {
2933 global $langs, $errormsg;
2934 $langs->load("errors");
2935 $errormsg = $langs->trans("ErrorFailedToWriteInDir", $outputfile);
2936 return -3;
2937 }
2938
2939 try {
2940 if ($mode == 'gz') {
2941 $foundhandler = 0;
2942 } elseif ($mode == 'bz') {
2943 $foundhandler = 0;
2944 } elseif ($mode == 'zip') {
2945 /*if (defined('ODTPHP_PATHTOPCLZIP'))
2946 {
2947 $foundhandler=0; // TODO implement this
2948
2949 include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
2950 $archive = new PclZip($outputfile);
2951 $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
2952 //$archive->add($inputfile);
2953 return 1;
2954 }
2955 else*/
2956 //if (class_exists('ZipArchive') && !empty($conf->global->MAIN_USE_ZIPARCHIVE_FOR_ZIP_COMPRESS))
2957
2958 if (class_exists('ZipArchive')) {
2959 $foundhandler = 1;
2960
2961 // Initialize archive object
2962 $zip = new ZipArchive();
2963 $result = $zip->open($outputfile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
2964 if ($result !== true) {
2965 global $langs, $errormsg;
2966 $langs->load("errors");
2967 $errormsg = $langs->trans("ErrorFailedToBuildArchive", $outputfile);
2968 return -4;
2969 }
2970
2971 // Create recursive directory iterator
2972 // This does not return symbolic links
2974 $files = new RecursiveIteratorIterator(
2975 new RecursiveDirectoryIterator($inputdir, FilesystemIterator::UNIX_PATHS),
2976 RecursiveIteratorIterator::LEAVES_ONLY
2977 );
2978 '@phan-var-force SplFileInfo[] $files';
2979
2980 //var_dump($inputdir);
2981 foreach ($files as $name => $file) {
2982 // Skip directories (they would be added automatically)
2983 if (!$file->isDir()) {
2984 // Get real and relative path for current file
2985 $filePath = $file->getPath(); // the full path with filename using the $inputdir root.
2986 $fileName = $file->getFilename();
2987 $fileFullRealPath = $file->getRealPath(); // the full path with name and transformed to use real path directory.
2988
2989 //$relativePath = ($rootdirinzip ? $rootdirinzip.'/' : '').substr($fileFullRealPath, strlen($inputdir) + 1);
2990 $relativePath = ($rootdirinzip ? $rootdirinzip.'/' : '').substr(($filePath ? $filePath.'/' : '').$fileName, strlen($inputdir) + 1);
2991
2992 //var_dump($filePath);var_dump($fileFullRealPath);var_dump($relativePath);
2993 if (empty($excludefiles) || !preg_match($excludefiles, $fileFullRealPath)) {
2994 // Add current file to archive
2995 $zip->addFile($fileFullRealPath, $relativePath);
2996 }
2997 }
2998 }
2999
3000 // Zip archive will be created only after closing object
3001 $zip->close();
3002
3003 if (empty($newmask) && getDolGlobalString('MAIN_UMASK')) {
3004 $newmask = getDolGlobalString('MAIN_UMASK');
3005 }
3006 if (empty($newmask)) { // This should no happen
3007 dol_syslog("Warning: dol_compress_dir called with empty value for newmask and no default value defined", LOG_WARNING);
3008 $newmask = '0664';
3009 }
3010
3011 dolChmod($outputfile, $newmask);
3012
3013 return 1;
3014 }
3015 }
3016
3017 if (!$foundhandler) {
3018 dol_syslog("Try to zip with format ".$mode." with no handler for this format", LOG_ERR);
3019 return -2;
3020 } else {
3021 return 0;
3022 }
3023 } catch (Exception $e) {
3024 global $langs, $errormsg;
3025 $langs->load("errors");
3026 dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
3027 dol_syslog($e->getMessage(), LOG_ERR);
3028 $errormsg = $langs->trans("ErrorFailedToBuildArchive", $outputfile).' - '.$e->getMessage();
3029 return -1;
3030 }
3031}
3032
3033
3034
3045function dol_most_recent_file($dir, $regexfilter = '', $excludefilter = array('(\.meta|_preview.*\.png)$', '^\.'), $nohook = 0, $mode = 0)
3046{
3047 $tmparray = dol_dir_list($dir, 'files', 0, $regexfilter, $excludefilter, 'date', SORT_DESC, $mode, $nohook);
3048 return isset($tmparray[0]) ? $tmparray[0] : null;
3049}
3050
3064function dol_check_secure_access_document($modulepart, $original_file, $entity, $fuser = null, $refname = '', $mode = 'read')
3065{
3066 global $conf, $db, $user, $hookmanager;
3067 global $dolibarr_main_data_root, $dolibarr_main_document_root_alt;
3068 global $object;
3069
3070 if (!is_object($fuser)) {
3071 $fuser = $user;
3072 }
3073
3074 if (empty($modulepart)) {
3075 return 'ErrorBadParameter';
3076 }
3077 if (empty($entity)) {
3078 if (!isModEnabled('multicompany')) {
3079 $entity = 1;
3080 } else {
3081 $entity = 0;
3082 }
3083 } else {
3084 // TODO Test that the user in session of conf->entity can see objects of the target $entity
3085 // ...
3086 }
3087 // Fix modulepart for backward compatibility
3088 if ($modulepart == 'facture') {
3089 $modulepart = 'invoice';
3090 } elseif ($modulepart == 'users') {
3091 $modulepart = 'user';
3092 } elseif ($modulepart == 'tva') {
3093 $modulepart = 'tax-vat';
3094 } elseif ($modulepart == 'expedition' && strpos($original_file, 'receipt/') === 0) {
3095 // Fix modulepart delivery
3096 $modulepart = 'delivery';
3097 } elseif ($modulepart == 'propale') {
3098 $modulepart = 'propal';
3099 }
3100
3101 //print 'dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity;
3102 dol_syslog('dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity);
3103
3104 // We define $accessallowed and $sqlprotectagainstexternals
3105 $accessallowed = 0;
3106 $sqlprotectagainstexternals = '';
3107 $ret = array();
3108
3109 // Find the subdirectory name as the reference. For example original_file='10/myfile.pdf' -> refname='10'
3110 if (empty($refname)) {
3111 $refname = basename(dirname($original_file)."/");
3112 if ($refname == 'thumbs' || $refname == 'temp') {
3113 // If we get the thumbs directory, we must go one step higher. For example original_file='10/thumbs/myfile_small.jpg' -> refname='10'
3114 $refname = basename(dirname(dirname($original_file))."/");
3115 }
3116 }
3117
3118 // Define possible keys to use for permission check
3119 $lire = 'lire';
3120 $read = 'read';
3121 $download = 'download';
3122 if ($mode == 'write') {
3123 $lire = 'creer';
3124 $read = 'write';
3125 $download = 'upload';
3126 }
3127
3128 // Wrapping for miscellaneous medias files
3129 if ($modulepart == 'common') {
3130 // Wrapping for some images
3131 $accessallowed = 1;
3132 $original_file = DOL_DOCUMENT_ROOT.'/public/theme/common/'.$original_file;
3133 } elseif ($modulepart == 'medias' && !empty($dolibarr_main_data_root)) {
3134 /* 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 */
3135 if (empty($entity)) {
3136 $entity = 1;
3137 }
3138 $accessallowed = 0;
3139 if ($mode == 'write') {
3140 if ($fuser->hasRight('website', 'write')) {
3141 $accessallowed = 1;
3142 }
3143 } else {
3144 $accessallowed = 1; // As dir is public, we allow read access to all files in medias directory
3145 }
3146 $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;
3147 } elseif ($modulepart == 'logs' && !empty($dolibarr_main_data_root)) {
3148 // Wrapping for *.log files, like when used with url http://.../document.php?modulepart=logs&file=dolibarr.log
3149 $accessallowed = ($user->admin && basename($original_file) == $original_file && preg_match('/^dolibarr.*\.(log|json)$/', basename($original_file)));
3150 $original_file = $dolibarr_main_data_root.'/'.$original_file;
3151 } elseif ($modulepart == 'doctemplates' && !empty($dolibarr_main_data_root)) {
3152 $accessallowed = $user->admin;
3153 $relative_file = $original_file;
3154 $ent = ($entity > 0 ? $entity : $conf->entity);
3155 $path_with_entity = $dolibarr_main_data_root . '/' . $ent . '/doctemplates/' . $relative_file;
3156 if ($ent > 1 && file_exists(dol_osencode($path_with_entity))) {
3157 $original_file = $path_with_entity;
3158 } else {
3159 $original_file = $dolibarr_main_data_root . '/doctemplates/' . $relative_file;
3160 }
3161 } elseif ($modulepart == 'doctemplateswebsite' && !empty($dolibarr_main_data_root)) {
3162 // Wrapping for doctemplates of websites
3163 $accessallowed = ($fuser->hasRight('website', 'write') && preg_match('/\.jpg$/i', basename($original_file)));
3164 $original_file = $dolibarr_main_data_root.'/doctemplates/websites/'.$original_file;
3165 } elseif ($modulepart == 'packages' && !empty($dolibarr_main_data_root)) { // To download zip of modules
3166 // Wrapping for *.zip package files, like when used with url http://.../document.php?modulepart=packages&file=module_myfile.zip
3167 // Dir for custom dirs
3168 $tmp = explode(',', $dolibarr_main_document_root_alt);
3169 $dirins = $tmp[0];
3170
3171 $accessallowed = ($user->admin && preg_match('/^module_.*\.zip$/', basename($original_file)));
3172 $original_file = $dirins.'/'.$original_file;
3173 } elseif ($modulepart == 'mycompany' && !empty($conf->mycompany->dir_output)) {
3174 // Wrapping for some images
3175 $accessallowed = 1;
3176 $original_file = $conf->mycompany->dir_output.'/'.$original_file;
3177 } elseif ($modulepart == 'userphoto' && !empty($conf->user->dir_output)) {
3178 // Wrapping for users photos (user photos are allowed to any connected users)
3179 $accessallowed = 0;
3180 if (preg_match('/^\d+\/photos\//', $original_file)) {
3181 $accessallowed = 1;
3182 }
3183 $original_file = $conf->user->dir_output.'/'.$original_file;
3184 } elseif ($modulepart == 'userphotopublic' && !empty($conf->user->dir_output)) {
3185 // Wrapping for users photos that were set to public (for virtual credit card) by their owner (public user photos can be read
3186 // with the public link and securekey)
3187 $accessok = false;
3188 $reg = array();
3189 if (preg_match('/^(\d+)\/photos\//', $original_file, $reg)) {
3190 if ((int) $reg[1]) {
3191 $tmpobject = new User($db);
3192 $tmpobject->fetch((int) $reg[1], '', '', 1);
3193 if (getDolUserInt('USER_ENABLE_PUBLIC', 0, $tmpobject)) {
3194 $securekey = GETPOST('securekey', 'alpha', 1);
3195 // Security check
3196 global $dolibarr_main_cookie_cryptkey, $dolibarr_main_instance_unique_id;
3197 $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
3198 $encodedsecurekey = dol_hash($valuetouse.'uservirtualcard'.$tmpobject->id.'-'.$tmpobject->login, 'md5');
3199 if ($encodedsecurekey == $securekey) {
3200 $accessok = true;
3201 }
3202 }
3203 }
3204 }
3205 if ($accessok) {
3206 $accessallowed = 1;
3207 }
3208 $original_file = $conf->user->dir_output.'/'.$original_file;
3209 } elseif (($modulepart == 'companylogo') && !empty($conf->mycompany->dir_output)) {
3210 // Wrapping for company logos (company logos are allowed to anyboby, they are public)
3211 $accessallowed = 1;
3212 $original_file = $conf->mycompany->dir_output.'/logos/'.$original_file;
3213 } elseif ($modulepart == 'memberphoto' && !empty($conf->member->dir_output)) {
3214 // Wrapping for members photos
3215 $accessallowed = 0;
3216 // Simple chosen for automatic generation of member codes
3217 if (preg_match('/^\d+\/photos\//', $original_file)) {
3218 $accessallowed = 1;
3219 }
3220 // Advanced chosen for automatic generation of member codes
3221 if (preg_match('/^MEM\d\d\d\d-\d\d\d\d\/photos\//', $original_file)) {
3222 $accessallowed = 1;
3223 }
3224 $original_file = $conf->member->dir_output.'/'.$original_file;
3225 } elseif ($modulepart == 'apercufacture' && !empty($conf->invoice->multidir_output[$entity])) {
3226 // Wrapping for invoices (user need permission to read invoices)
3227 if ($fuser->hasRight('facture', $lire)) {
3228 $accessallowed = 1;
3229 }
3230 $original_file = $conf->invoice->multidir_output[$entity].'/'.$original_file;
3231 } elseif ($modulepart == 'apercupropal' && !empty($conf->propal->multidir_output[$entity])) {
3232 // Wrapping for preview of proposals
3233 if ($fuser->hasRight('propal', $lire)) {
3234 $accessallowed = 1;
3235 }
3236 $original_file = $conf->propal->multidir_output[$entity].'/'.$original_file;
3237 } elseif ($modulepart == 'apercucommande' && !empty($conf->order->multidir_output[$entity])) {
3238 // Wrapping for preview of orders
3239 if ($fuser->hasRight('commande', $lire)) {
3240 $accessallowed = 1;
3241 }
3242 $original_file = $conf->order->multidir_output[$entity].'/'.$original_file;
3243 } elseif (($modulepart == 'apercufichinter' || $modulepart == 'apercuficheinter') && !empty($conf->ficheinter->multidir_output[$entity])) {
3244 // Wrapping for preview of intervention
3245 if ($fuser->hasRight('ficheinter', $lire)) {
3246 $accessallowed = 1;
3247 }
3248 $original_file = $conf->ficheinter->multidir_output[$entity].'/'.$original_file;
3249 } elseif (($modulepart == 'apercucontract') && !empty($conf->contract->multidir_output[$entity])) {
3250 // Wrapping for preview of contracts
3251 if ($fuser->hasRight('contrat', $lire)) {
3252 $accessallowed = 1;
3253 }
3254 $original_file = $conf->contract->multidir_output[$entity].'/'.$original_file;
3255 } elseif (($modulepart == 'apercusupplier_proposal') && !empty($conf->supplier_proposal->dir_output)) {
3256 // Wrapping for preview of vendor proposals
3257 if ($fuser->hasRight('supplier_proposal', $lire)) {
3258 $accessallowed = 1;
3259 }
3260 $original_file = $conf->supplier_proposal->dir_output.'/'.$original_file;
3261 } elseif (($modulepart == 'apercusupplier_order') && !empty($conf->fournisseur->commande->dir_output)) {
3262 // Wrapping for preview of purchase orders
3263 if ($fuser->hasRight('fournisseur', 'commande', $lire)) {
3264 $accessallowed = 1;
3265 }
3266 $original_file = $conf->fournisseur->commande->dir_output.'/'.$original_file;
3267 } elseif (($modulepart == 'apercusupplier_invoice') && !empty($conf->fournisseur->facture->dir_output)) {
3268 // Wrapping for preview of supplier invoices
3269 if ($fuser->hasRight('fournisseur', $lire)) {
3270 $accessallowed = 1;
3271 }
3272 $original_file = $conf->fournisseur->facture->dir_output.'/'.$original_file;
3273 } elseif (($modulepart == 'holiday') && !empty($conf->holiday->dir_output)) {
3274 if ($fuser->hasRight('holiday', $read) || $fuser->hasRight('holiday', 'readall') || preg_match('/^specimen/i', $original_file)) {
3275 $accessallowed = 1;
3276 // If we known $id of holiday, call checkUserAccessToObject to check permission on properties and hierarchy of leave request
3277 if ($refname && !$fuser->hasRight('holiday', 'readall') && !preg_match('/^specimen/i', $original_file)) {
3278 include_once DOL_DOCUMENT_ROOT.'/holiday/class/holiday.class.php';
3279 $tmpholiday = new Holiday($db);
3280 $tmpholiday->fetch(0, $refname);
3281 $accessallowed = checkUserAccessToObject($user, array('holiday'), $tmpholiday, 'holiday', '', '', 'rowid', '');
3282 }
3283 }
3284 $original_file = $conf->holiday->dir_output.'/'.$original_file;
3285 } elseif (($modulepart == 'salaries') && !empty($conf->salaries->dir_output)) {
3286 // Wrapping for salaries. The subdirectory is the id of the salary, see salaries/document.php.
3287 if ($fuser->hasRight('salaries', $read) || $fuser->hasRight('salaries', 'readall') || preg_match('/^specimen/i', $original_file)) {
3288 $accessallowed = 1;
3289 // The 'read' permission is labelled "yours only" and the two screens leading to this
3290 // download, salaries/card.php and salaries/document.php, do enforce it on fk_user.
3291 // checkUserAccessToObject() only tests the entity for this feature, so the same rule has to
3292 // be applied here: without it any holder of salaries->read downloads every payslip.
3293 if ($refname && !$fuser->hasRight('salaries', 'readall') && !preg_match('/^specimen/i', $original_file)) {
3294 include_once DOL_DOCUMENT_ROOT.'/salaries/class/salary.class.php';
3295 $tmpsalary = new Salary($db);
3296 $tmpsalary->fetch((int) $refname);
3297 // Same condition as salaries/card.php and salaries/document.php, word for word.
3298 // getAllChildIds(1) includes the current user, so this covers their own payslip as well
3299 // as those of the users below them. The test on fk_user also closes the case of an id
3300 // that matches no salary, Salary::fetch() returning 1 even then.
3301 $accessallowed = ($tmpsalary->fk_user > 0 && in_array($tmpsalary->fk_user, $fuser->getAllChildIds(1))) ? 1 : 0;
3302 }
3303 }
3304 $original_file = $conf->salaries->dir_output.'/'.$original_file;
3305 } elseif (($modulepart == 'expensereport') && !empty($conf->expensereport->dir_output)) {
3306 if ($fuser->hasRight('expensereport', $lire) || $fuser->hasRight('expensereport', 'readall') || preg_match('/^specimen/i', $original_file)) {
3307 $accessallowed = 1;
3308 // If we known $id of expensereport, call checkUserAccessToObject to check permission on properties and hierarchy of expense report
3309 if ($refname && !$fuser->hasRight('expensereport', 'readall') && !preg_match('/^specimen/i', $original_file)) {
3310 include_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
3311 $tmpexpensereport = new ExpenseReport($db);
3312 $tmpexpensereport->fetch(0, $refname);
3313 $accessallowed = checkUserAccessToObject($user, array('expensereport'), $tmpexpensereport, 'expensereport', '', '', 'rowid', '');
3314 }
3315 }
3316 $original_file = $conf->expensereport->dir_output.'/'.$original_file;
3317 } elseif (($modulepart == 'apercuexpensereport') && !empty($conf->expensereport->dir_output)) {
3318 // Wrapping for preview of expense report
3319 if ($fuser->hasRight('expensereport', $lire)) {
3320 $accessallowed = 1;
3321 }
3322 $original_file = $conf->expensereport->dir_output.'/'.$original_file;
3323 } elseif ($modulepart == 'propalstats' && !empty($conf->propal->multidir_temp[$entity])) {
3324 // Wrapping for statistics images of proposal
3325 if ($fuser->hasRight('propal', $lire)) {
3326 $accessallowed = 1;
3327 }
3328 $original_file = $conf->propal->multidir_temp[$entity].'/'.$original_file;
3329 } elseif ($modulepart == 'orderstats' && !empty($conf->order->dir_temp)) {
3330 // Wrapping for statistics images of orders
3331 if ($fuser->hasRight('commande', $lire)) {
3332 $accessallowed = 1;
3333 }
3334 $original_file = $conf->order->dir_temp.'/'.$original_file;
3335 } elseif ($modulepart == 'orderstatssupplier' && !empty($conf->fournisseur->dir_output)) {
3336 if ($fuser->hasRight('fournisseur', 'commande', $lire)) {
3337 $accessallowed = 1;
3338 }
3339 $original_file = $conf->fournisseur->commande->dir_temp.'/'.$original_file;
3340 } elseif ($modulepart == 'billstats' && !empty($conf->invoice->dir_temp)) {
3341 // Wrapping for statistics images of purchase orders
3342 if ($fuser->hasRight('facture', $lire)) {
3343 $accessallowed = 1;
3344 }
3345 $original_file = $conf->invoice->dir_temp.'/'.$original_file;
3346 } elseif ($modulepart == 'billstatssupplier' && !empty($conf->fournisseur->dir_output)) {
3347 if ($fuser->hasRight('fournisseur', 'facture', $lire)) {
3348 $accessallowed = 1;
3349 }
3350 $original_file = $conf->fournisseur->facture->dir_temp.'/'.$original_file;
3351 } elseif ($modulepart == 'expeditionstats' && !empty($conf->expedition->dir_temp)) {
3352 // Wrapping for statistics images of shipments
3353 if ($fuser->hasRight('expedition', $lire)) {
3354 $accessallowed = 1;
3355 }
3356 $original_file = $conf->expedition->dir_temp.'/'.$original_file;
3357 } elseif ($modulepart == 'tripsexpensesstats' && !empty($conf->deplacement->dir_temp)) {
3358 // Wrapping pour les images des stats expeditions
3359 if ($fuser->hasRight('deplacement', $lire)) {
3360 $accessallowed = 1;
3361 }
3362 $original_file = $conf->deplacement->dir_temp.'/'.$original_file;
3363 } elseif ($modulepart == 'memberstats' && !empty($conf->member->dir_temp)) {
3364 // Wrapping for statistics images of memberships
3365 if ($fuser->hasRight('adherent', $lire)) {
3366 $accessallowed = 1;
3367 }
3368 $original_file = $conf->member->dir_temp.'/'.$original_file;
3369 } elseif (preg_match('/^productstats_/i', $modulepart) && !empty($conf->product->dir_temp)) {
3370 // Wrapping for statistics images of products
3371 if ($fuser->hasRight('produit', $lire) || $fuser->hasRight('service', $lire)) {
3372 $accessallowed = 1;
3373 }
3374 $original_file = (!empty($conf->product->multidir_temp[$entity]) ? $conf->product->multidir_temp[$entity] : $conf->service->multidir_temp[$entity]).'/'.$original_file;
3375 } elseif (in_array($modulepart, array('tax', 'tax-vat', 'tva')) && !empty($conf->tax->dir_output)) {
3376 // Wrapping for taxes
3377 if ($fuser->hasRight('tax', 'charges', $lire)) {
3378 $accessallowed = 1;
3379 }
3380 $modulepartsuffix = str_replace('tax-', '', $modulepart);
3381 $original_file = $conf->tax->dir_output.'/'.($modulepartsuffix != 'tax' ? $modulepartsuffix.'/' : '').$original_file;
3382 } elseif (($modulepart == 'actions' || $modulepart == 'actioncomm') && !empty($conf->agenda->dir_output)) {
3383 // Wrapping for events
3384 if ($fuser->hasRight('agenda', 'myactions', $read)) {
3385 $accessallowed = 1;
3386 // If we known $id of project, call checkUserAccessToObject to check permission on the given agenda event on properties and assigned users
3387 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3388 include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
3389 $tmpobject = new ActionComm($db);
3390 $tmpobject->fetch((int) $refname);
3391 $accessallowed = checkUserAccessToObject($user, array('agenda'), $tmpobject->id, 'actioncomm&societe', 'myactions|allactions', 'fk_soc', 'id', '');
3392 if ($user->socid && $tmpobject->socid) {
3393 $accessallowed = checkUserAccessToObject($user, array('societe'), $tmpobject->socid);
3394 }
3395 }
3396 }
3397 $original_file = $conf->agenda->dir_output.'/'.$original_file;
3398 } elseif ($modulepart == 'category' && !empty($conf->categorie->multidir_output[$entity])) {
3399 // Wrapping for categories (categories are allowed if user has permission to read categories or to work on TakePos)
3400 if (empty($entity) || empty($conf->categorie->multidir_output[$entity])) {
3401 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3402 }
3403 if ($fuser->hasRight("categorie", $lire) || $fuser->hasRight("takepos", "run")) {
3404 $accessallowed = 1;
3405 }
3406 $original_file = $conf->categorie->multidir_output[$entity].'/'.$original_file;
3407 } elseif ($modulepart == 'prelevement' && !empty($conf->prelevement->dir_output)) {
3408 // Wrapping pour les prelevements
3409 if ($fuser->hasRight('prelevement', 'bons', $lire) || preg_match('/^specimen/i', $original_file)) {
3410 $accessallowed = 1;
3411 }
3412 $original_file = $conf->prelevement->dir_output.'/'.$original_file;
3413 } elseif ($modulepart == 'graph_stock' && !empty($conf->stock->dir_temp)) {
3414 // Wrapping pour les graph energie
3415 $accessallowed = 1;
3416 $original_file = $conf->stock->dir_temp.'/'.$original_file;
3417 } elseif ($modulepart == 'graph_fourn' && !empty($conf->fournisseur->dir_temp)) {
3418 // Wrapping pour les graph fournisseurs
3419 $accessallowed = 1;
3420 $original_file = $conf->fournisseur->dir_temp.'/'.$original_file;
3421 } elseif ($modulepart == 'graph_product' && !empty($conf->product->dir_temp)) {
3422 // Wrapping pour les graph des produits
3423 $accessallowed = 1;
3424 $original_file = $conf->product->multidir_temp[$entity].'/'.$original_file;
3425 } elseif ($modulepart == 'barcode') {
3426 // Wrapping pour les code barre
3427 $accessallowed = 1;
3428 // If viewimage is called for barcode, we try to output an image on the fly, with no build of file on disk.
3429 //$original_file=$conf->barcode->dir_temp.'/'.$original_file;
3430 $original_file = '';
3431 } elseif ($modulepart == 'iconmailing' && !empty($conf->mailing->dir_temp)) {
3432 // Wrapping for icon of background of mailings
3433 $accessallowed = 1;
3434 $original_file = $conf->mailing->dir_temp.'/'.$original_file;
3435 } elseif ($modulepart == 'scanner_user_temp' && !empty($conf->scanner->dir_temp)) {
3436 // Wrapping pour le scanner
3437 $accessallowed = 1;
3438 $original_file = $conf->scanner->dir_temp.'/'.$fuser->id.'/'.$original_file;
3439 } elseif ($modulepart == 'fckeditor' && !empty($conf->fckeditor->dir_output)) {
3440 // Wrapping pour les images fckeditor
3441 $accessallowed = 1;
3442 $original_file = $conf->fckeditor->dir_output.'/'.$original_file;
3443 } elseif ($modulepart == 'user' && !empty($conf->user->dir_output)) {
3444 // Wrapping for users
3445 $canreaduser = (!empty($fuser->admin) || $fuser->hasRight('user', 'user', $lire));
3446 if ($fuser->id == (int) $refname) {
3447 $canreaduser = 1;
3448 } // A user can always read its own card
3449 if ($canreaduser || preg_match('/^specimen/i', $original_file)) {
3450 $accessallowed = 1;
3451 }
3452 $original_file = $conf->user->dir_output.'/'.$original_file;
3453 } elseif (($modulepart == 'company' || $modulepart == 'societe' || $modulepart == 'thirdparty') && !empty($conf->societe->multidir_output[$entity])) {
3454 // Wrapping for third parties
3455 if (empty($entity) || empty($conf->societe->multidir_output[$entity])) {
3456 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3457 }
3458 if ($fuser->hasRight('societe', $lire) || preg_match('/^specimen/i', $original_file)) {
3459 $accessallowed = 1;
3460 }
3461 $original_file = $conf->societe->multidir_output[$entity].'/'.$original_file;
3462 $sqlprotectagainstexternals = "SELECT rowid as fk_soc FROM ".MAIN_DB_PREFIX."societe WHERE rowid = ".((int) $refname)." AND entity IN (".getEntity('societe').")";
3463 } elseif (($modulepart == 'contact' || $modulepart == 'socpeople') && !empty($conf->societe->multidir_output[$entity])) {
3464 // Wrapping for contact
3465 if (empty($entity) || empty($conf->societe->multidir_output[$entity])) {
3466 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3467 }
3468 if ($fuser->hasRight('societe', 'contact', $lire)) {
3469 $accessallowed = 1;
3470 }
3471 $original_file = $conf->societe->multidir_output[$entity].'/contact/'.$original_file;
3472 $sqlprotectagainstexternals = "SELECT fk_soc FROM ".MAIN_DB_PREFIX."socpeople WHERE rowid = ".((int) $refname)." AND entity IN (".getEntity('contact').")";
3473 } elseif (($modulepart == 'facture' || $modulepart == 'invoice') && !empty($conf->invoice->multidir_output[$entity])) {
3474 // Wrapping for invoices
3475 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3476 $accessallowed = 1;
3477 }
3478 $original_file = $conf->invoice->multidir_output[$entity].'/'.$original_file;
3479 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('invoice').")";
3480 } elseif ($modulepart == 'massfilesarea_proposals' && !empty($conf->propal->multidir_output[$entity])) {
3481 // Wrapping for mass actions
3482 if ($fuser->hasRight('propal', $lire) || preg_match('/^specimen/i', $original_file)) {
3483 $accessallowed = 1;
3484 }
3485 $original_file = $conf->propal->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3486 } elseif ($modulepart == 'massfilesarea_orders') {
3487 if ($fuser->hasRight('commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3488 $accessallowed = 1;
3489 }
3490 $original_file = $conf->order->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3491 } elseif ($modulepart == 'massfilesarea_sendings') {
3492 if ($fuser->hasRight('expedition', $lire) || preg_match('/^specimen/i', $original_file)) {
3493 $accessallowed = 1;
3494 }
3495 $original_file = $conf->expedition->dir_output.'/sending/temp/massgeneration/'.$user->id.'/'.$original_file;
3496 } elseif ($modulepart == 'massfilesarea_receipts') {
3497 if ($fuser->hasRight('reception', $lire) || preg_match('/^specimen/i', $original_file)) {
3498 $accessallowed = 1;
3499 }
3500 $original_file = $conf->reception->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3501 } elseif ($modulepart == 'massfilesarea_invoices') {
3502 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3503 $accessallowed = 1;
3504 }
3505 $original_file = $conf->invoice->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file;
3506 } elseif ($modulepart == 'massfilesarea_expensereport') {
3507 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3508 $accessallowed = 1;
3509 }
3510 $original_file = $conf->expensereport->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3511 } elseif ($modulepart == 'massfilesarea_interventions') {
3512 if ($fuser->hasRight('ficheinter', $lire) || preg_match('/^specimen/i', $original_file)) {
3513 $accessallowed = 1;
3514 }
3515 $original_file = $conf->ficheinter->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3516 } elseif ($modulepart == 'massfilesarea_supplier_proposal' && !empty($conf->supplier_proposal->dir_output)) {
3517 if ($fuser->hasRight('supplier_proposal', $lire) || preg_match('/^specimen/i', $original_file)) {
3518 $accessallowed = 1;
3519 }
3520 $original_file = $conf->supplier_proposal->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3521 } elseif ($modulepart == 'massfilesarea_supplier_order') {
3522 if ($fuser->hasRight('fournisseur', 'commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3523 $accessallowed = 1;
3524 }
3525 $original_file = $conf->fournisseur->commande->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3526 } elseif ($modulepart == 'massfilesarea_supplier_invoice') {
3527 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3528 $accessallowed = 1;
3529 }
3530 $original_file = $conf->fournisseur->facture->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3531 } elseif ($modulepart == 'massfilesarea_contract' && !empty($conf->contract->dir_output)) {
3532 if ($fuser->hasRight('contrat', $lire) || preg_match('/^specimen/i', $original_file)) {
3533 $accessallowed = 1;
3534 }
3535 $original_file = $conf->contract->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3536 } elseif ($modulepart == 'massfilesarea_stock' && !empty($conf->stock->dir_output)) {
3537 if ($fuser->hasRight('stock', $lire) || preg_match('/^specimen/i', $original_file)) {
3538 $accessallowed = 1;
3539 }
3540 $original_file = $conf->stock->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3541 } elseif (($modulepart == 'fichinter' || $modulepart == 'ficheinter') && !empty($conf->ficheinter->multidir_output[$entity])) {
3542 // Wrapping for interventions
3543 if ($fuser->hasRight('ficheinter', $lire) || preg_match('/^specimen/i', $original_file)) {
3544 $accessallowed = 1;
3545 }
3546 $original_file = $conf->ficheinter->multidir_output[$entity].'/'.$original_file;
3547 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3548 } elseif ($modulepart == 'deplacement' && !empty($conf->deplacement->dir_output)) {
3549 // Wrapping pour les deplacements et notes de frais
3550 if ($fuser->hasRight('deplacement', $lire) || preg_match('/^specimen/i', $original_file)) {
3551 $accessallowed = 1;
3552 }
3553 $original_file = $conf->deplacement->dir_output.'/'.$original_file;
3554 //$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3555 } elseif (($modulepart == 'propal' || $modulepart == 'propale') && isset($conf->propal->multidir_output[$entity])) {
3556 // Wrapping pour les propales
3557 if ($fuser->hasRight('propal', $lire) || preg_match('/^specimen/i', $original_file)) {
3558 $accessallowed = 1;
3559 }
3560 $original_file = $conf->propal->multidir_output[$entity].'/'.$original_file;
3561 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."propal WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('propal').")";
3562 } elseif (($modulepart == 'commande' || $modulepart == 'order') && !empty($conf->order->multidir_output[$entity])) {
3563 // Wrapping pour les commandes
3564 if ($fuser->hasRight('commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3565 $accessallowed = 1;
3566 }
3567 $original_file = $conf->order->multidir_output[$entity].'/'.$original_file;
3568 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('order').")";
3569 } elseif ($modulepart == 'project' && !empty($conf->project->multidir_output[$entity])) {
3570 // Wrapping pour les projects
3571 if ($fuser->hasRight('projet', $lire) || preg_match('/^specimen/i', $original_file)) {
3572 $accessallowed = 1;
3573 // If we known $id of project, call checkUserAccessToObject to check permission on properties and contact of project
3574 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3575 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
3576 $tmpproject = new Project($db);
3577 $tmpproject->fetch(0, $refname);
3578 $accessallowed = checkUserAccessToObject($user, array('projet'), $tmpproject->id, 'projet&project', '', '', 'rowid', '');
3579 }
3580 }
3581 $original_file = $conf->project->multidir_output[$entity].'/'.$original_file;
3582 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
3583 } elseif ($modulepart == 'project_task' && !empty($conf->project->multidir_output[$entity])) {
3584 if ($fuser->hasRight('projet', $lire) || preg_match('/^specimen/i', $original_file)) {
3585 $accessallowed = 1;
3586 // If we known $id of project, call checkUserAccessToObject to check permission on properties and contact of project
3587 if ($refname && !preg_match('/^specimen/i', $original_file)) {
3588 include_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
3589 $tmptask = new Task($db);
3590 $tmptask->fetch(0, $refname);
3591 $accessallowed = checkUserAccessToObject($user, array('projet_task'), $tmptask->id, 'projet_task&project', '', '', 'rowid', '');
3592 }
3593 }
3594 $original_file = $conf->project->multidir_output[$entity].'/'.$original_file;
3595 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
3596 } elseif (($modulepart == 'commande_fournisseur' || $modulepart == 'order_supplier' || $modulepart == 'supplier_order') && !empty($conf->fournisseur->commande->dir_output)) {
3597 // Wrapping for purchase orders
3598 if ($fuser->hasRight('fournisseur', 'commande', $lire) || preg_match('/^specimen/i', $original_file)) {
3599 $accessallowed = 1;
3600 }
3601 $original_file = $conf->fournisseur->commande->dir_output.'/'.$original_file;
3602 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE ref='".$db->escape($refname)."' AND entity = ".((int) $conf->entity);
3603 } elseif (($modulepart == 'facture_fournisseur' || $modulepart == 'invoice_supplier' || $modulepart == 'supplier_invoice') && !empty($conf->fournisseur->facture->dir_output)) {
3604 // Wrapping for supplier invoices
3605 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3606 $accessallowed = 1;
3607 }
3608 $original_file = $conf->fournisseur->facture->dir_output.'/'.$original_file;
3609 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture_fourn WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3610 } elseif ($modulepart == 'supplier_payment') {
3611 // Wrapping for supplier payments
3612 if ($fuser->hasRight('fournisseur', 'facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3613 $accessallowed = 1;
3614 }
3615 $original_file = preg_replace("/payment\//", "", $original_file); // Because the $conf->fournisseur->payment->dir_output already contains the "payment/"
3616 $original_file = $conf->fournisseur->payment->dir_output.'/'.$original_file;
3617 $sqlprotectagainstexternals = "SELECT f.fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."paiementfourn as p";
3618 $sqlprotectagainstexternals .= " INNER JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON pf.fk_paiementfourn = p.rowid";
3619 $sqlprotectagainstexternals .= " INNER JOIN ".MAIN_DB_PREFIX."facture_fourn as f ON pf.fk_facturefourn = p.rowid";
3620 $sqlprotectagainstexternals .= " WHERE p.ref = '".$db->escape($refname)."' AND p.entity=".((int) $conf->entity);
3621 } elseif ($modulepart == 'payment') {
3622 // Wrapping for report of payments
3623 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3624 $accessallowed = 1;
3625 }
3626 $original_file = $conf->compta->payment->dir_output.'/'.$original_file;
3627 } elseif ($modulepart == 'facture_paiement' && !empty($conf->invoice->dir_output)) {
3628 // Wrapping for report of payments
3629 if ($fuser->hasRight('facture', $lire) || preg_match('/^specimen/i', $original_file)) {
3630 $accessallowed = 1;
3631 }
3632 if ($fuser->socid > 0) {
3633 $original_file = $conf->invoice->dir_output.'/payments/private/'.$fuser->id.'/'.$original_file;
3634 } else {
3635 $original_file = $conf->invoice->dir_output.'/payments/'.$original_file;
3636 }
3637 /* $sqlprotectagainstexternals = "SELECT f.fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."paiement as p";
3638 $sqlprotectagainstexternals .= " INNER JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON pf.fk_paiement = p.rowid";
3639 $sqlprotectagainstexternals .= " INNER JOIN ".MAIN_DB_PREFIX."facture as f ON pf.fk_facture = p.rowid";
3640 $sqlprotectagainstexternals .= " WHERE p.ref = '".$db->escape($refname)."' AND p.entity=".((int) $conf->entity);
3641 var_dump($sqlprotectagainstexternals);exit;*/
3642 } elseif ($modulepart == 'accounting' && !empty($conf->accounting->dir_output)) {
3643 // Wrapping for accounting exports
3644 if ($fuser->hasRight('accounting', 'bind', 'write') || $fuser->hasRight('accounting', 'mouvements', 'export') || preg_match('/^specimen/i', $original_file)) {
3645 $accessallowed = 1;
3646 }
3647 $original_file = $conf->accounting->dir_output.'/'.$original_file;
3648 } elseif (($modulepart == 'expedition' || $modulepart == 'shipment' || $modulepart == 'shipping') && !empty($conf->expedition->dir_output)) {
3649 // Wrapping pour les expedition
3650 if ($fuser->hasRight('expedition', $lire) || preg_match('/^specimen/i', $original_file)) {
3651 $accessallowed = 1;
3652 }
3653 $original_file = $conf->expedition->dir_output."/".(strpos($original_file, 'sending/') === 0 ? '' : 'sending/').$original_file;
3654 //$original_file = $conf->expedition->dir_output."/".$original_file;
3655 } elseif (($modulepart == 'livraison' || $modulepart == 'delivery') && !empty($conf->expedition->dir_output)) {
3656 // Delivery Note Wrapping
3657 if ($fuser->hasRight('expedition', 'delivery', $lire) || preg_match('/^specimen/i', $original_file)) {
3658 $accessallowed = 1;
3659 }
3660 $original_file = $conf->expedition->dir_output."/".(strpos($original_file, 'receipt/') === 0 ? '' : 'receipt/').$original_file;
3661 } elseif ($modulepart == 'actionsreport' && !empty($conf->agenda->dir_temp)) {
3662 // Wrapping for actions
3663 if ($fuser->hasRight('agenda', 'allactions', $read) || preg_match('/^specimen/i', $original_file)) {
3664 $accessallowed = 1;
3665 }
3666 $original_file = $conf->agenda->dir_temp."/".$original_file;
3667 } elseif ($modulepart == 'product' || $modulepart == 'produit' || $modulepart == 'service' || $modulepart == 'produit|service') {
3668 // Wrapping for products and services
3669 if (empty($entity) || (empty($conf->product->multidir_output[$entity]) && empty($conf->service->multidir_output[$entity]))) {
3670 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3671 }
3672 if (($fuser->hasRight('produit', $lire) || $fuser->hasRight('service', $lire)) || preg_match('/^specimen/i', $original_file)) {
3673 $accessallowed = 1;
3674 }
3675 if (isModEnabled("product")) {
3676 $original_file = $conf->product->multidir_output[$entity].'/'.$original_file;
3677 } elseif (isModEnabled("service")) {
3678 $original_file = $conf->service->multidir_output[$entity].'/'.$original_file;
3679 }
3680 } elseif ($modulepart == 'product_batch' || $modulepart == 'produitlot') {
3681 // Wrapping for product lots
3682 if (empty($entity) || (empty($conf->productbatch->multidir_output[$entity]))) {
3683 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3684 }
3685 if (($fuser->hasRight('produit', $lire)) || preg_match('/^specimen/i', $original_file)) {
3686 $accessallowed = 1;
3687 }
3688 if (isModEnabled('productbatch')) {
3689 $original_file = $conf->productbatch->multidir_output[$entity].'/'.$original_file;
3690 }
3691 } elseif ($modulepart == 'movement' || $modulepart == 'mouvement') {
3692 // Wrapping for stock movements
3693 if (empty($entity) || empty($conf->stock->multidir_output[$entity])) {
3694 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3695 }
3696 if (($fuser->hasRight('stock', $lire) || $fuser->hasRight('stock', 'movement', $lire) || $fuser->hasRight('stock', 'mouvement', $lire)) || preg_match('/^specimen/i', $original_file)) {
3697 $accessallowed = 1;
3698 }
3699 if (isModEnabled('stock')) {
3700 $original_file = $conf->stock->multidir_output[$entity].'/movement/'.$original_file;
3701 }
3702 } elseif ($modulepart == 'entrepot') {
3703 // Wrapping for stock warehouse
3704 if (empty($entity) || empty($conf->stock->multidir_output[$entity])) {
3705 return array('accessallowed' => 0, 'error' => 'Value entity must be provided');
3706 }
3707 if (($fuser->hasRight('stock', $lire) || $fuser->hasRight('stock', 'movement', $lire) || $fuser->hasRight('stock', 'mouvement', $lire)) || preg_match('/^specimen/i', $original_file)) {
3708 $accessallowed = 1;
3709 }
3710 if (isModEnabled('stock')) {
3711 $original_file = $conf->stock->multidir_output[$entity].'/'.$original_file;
3712 }
3713 } elseif ($modulepart == 'contract' && !empty($conf->contract->multidir_output[$entity])) {
3714 // Wrapping for contracts
3715 if ($fuser->hasRight('contrat', $lire) || preg_match('/^specimen/i', $original_file)) {
3716 $accessallowed = 1;
3717 }
3718 $original_file = $conf->contract->multidir_output[$entity].'/'.$original_file;
3719 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."contrat WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('contract').")";
3720 } elseif ($modulepart == 'donation' && !empty($conf->don->dir_output)) {
3721 // Wrapping for donation
3722 if ($fuser->hasRight('don', $lire) || preg_match('/^specimen/i', $original_file)) {
3723 $accessallowed = 1;
3724 }
3725 $original_file = $conf->don->dir_output.'/'.$original_file;
3726 } elseif ($modulepart == 'dolresource' && !empty($conf->resource->dir_output)) {
3727 // Wrapping for resources
3728 if ($fuser->hasRight('resource', $read) || preg_match('/^specimen/i', $original_file)) {
3729 $accessallowed = 1;
3730 }
3731 $original_file = $conf->resource->dir_output.'/'.$original_file;
3732 } elseif (($modulepart == 'remisecheque' || $modulepart == 'chequereceipt') && !empty($conf->bank->dir_output)) {
3733 // Wrapping pour les remises de cheques
3734 if ($fuser->hasRight('banque', $lire) || preg_match('/^specimen/i', $original_file)) {
3735 $accessallowed = 1;
3736 }
3737 $original_file = $conf->bank->dir_output.'/checkdeposits/'.$original_file; // original_file should contains relative path so include the get_exdir result
3738 } elseif (($modulepart == 'banque' || $modulepart == 'bank') && !empty($conf->bank->dir_output)) {
3739 // Wrapping for bank
3740 // The bank module has no 'creer' permission: writing a bank file requires 'modifier', like account_statement_document.php
3741 if ($fuser->hasRight('banque', ($mode == 'read' ? 'lire' : 'modifier'))) {
3742 $accessallowed = 1;
3743 }
3744 $original_file = $conf->bank->dir_output.'/'.$original_file;
3745 } elseif ($modulepart == 'export' && !empty($conf->export->dir_temp)) {
3746 // Wrapping for export module
3747 // Note that a test may not be required because we force the dir of download on the directory of the user that export
3748 $accessallowed = $user->hasRight('export', 'lire');
3749 $original_file = $conf->export->dir_temp.'/'.$fuser->id.'/'.$original_file;
3750 } elseif ($modulepart == 'import' && !empty($conf->import->dir_temp)) {
3751 // Wrapping for import module
3752 $accessallowed = $user->hasRight('import', 'run');
3753 $original_file = $conf->import->dir_temp.'/'.$original_file;
3754 } elseif ($modulepart == 'recruitment' && !empty($conf->recruitment->dir_output)) {
3755 // Wrapping for recruitment module
3756 $accessallowed = $user->hasRight('recruitment', 'recruitmentjobposition', 'read');
3757 $original_file = $conf->recruitment->dir_output.'/'.$original_file;
3758 } elseif ($modulepart == 'hrm' && !empty($conf->hrm->dir_output)) {
3759 // Wrapping for hrm module
3760 $accessallowed = $user->hasRight('hrm', 'all', 'read');
3761 $original_file = $conf->hrm->dir_output.'/'.$original_file;
3762 } elseif ($modulepart == 'editor' && !empty($conf->fckeditor->dir_output)) {
3763 // Wrapping for wysiwyg editor
3764 $accessallowed = 1;
3765 $original_file = $conf->fckeditor->dir_output.'/'.$original_file;
3766 } elseif ($modulepart == 'systemtools' && !empty($conf->admin->dir_output)) {
3767 // Wrapping for backups
3768 if ($fuser->admin) {
3769 $accessallowed = 1;
3770 }
3771 $original_file = $conf->admin->dir_output.'/'.$original_file;
3772 } elseif ($modulepart == 'admin_temp' && !empty($conf->admin->dir_temp)) {
3773 // Wrapping for upload file test
3774 if ($fuser->admin) {
3775 $accessallowed = 1;
3776 }
3777 $original_file = $conf->admin->dir_temp.'/'.$original_file;
3778 } elseif ($modulepart == 'bittorrent' && !empty($conf->bittorrent->dir_output)) {
3779 // Wrapping pour BitTorrent
3780 $accessallowed = 1;
3781 $dir = 'files';
3782 if (dol_mimetype($original_file) == 'application/x-bittorrent') {
3783 $dir = 'torrents';
3784 }
3785 $original_file = $conf->bittorrent->dir_output.'/'.$dir.'/'.$original_file;
3786 } elseif ($modulepart == 'member' && !empty($conf->member->dir_output)) {
3787 // Wrapping pour Foundation module
3788 if ($fuser->hasRight('adherent', $lire) || preg_match('/^specimen/i', $original_file)) {
3789 $accessallowed = 1;
3790 }
3791 $original_file = $conf->member->dir_output.'/'.$original_file;
3792 } elseif ($modulepart == 'ticket' && !empty($conf->ticket->multidir_output[$entity])) {
3793 // Wrapping for events
3794 if ($fuser->hasRight('ticket', $read)) {
3795 $accessallowed = 1;
3796 }
3797 if (!isset($_SESSION['email_customer'])) {
3798 // Request to check socid for external users
3799 $sqlprotectagainstexternals = "SELECT fk_soc FROM ".MAIN_DB_PREFIX."ticket WHERE ref='".$db->escape($refname)."' AND entity=".((int) $conf->entity);
3800 } else {
3801 $email_split = explode('@', $_SESSION['email_customer']);
3802
3803 $sqlprotectagainstexternals = 'SELECT t.rowid, t.fk_soc FROM '.MAIN_DB_PREFIX.'ticket t';
3804 $sqlprotectagainstexternals .= ' LEFT JOIN '.MAIN_DB_PREFIX.'element_contact ec ON ec.element_id = t.rowid';
3805 $sqlprotectagainstexternals .= ' LEFT JOIN '.MAIN_DB_PREFIX.'socpeople c ON c.rowid = ec.fk_socpeople';
3806 $sqlprotectagainstexternals .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_contact tc ON tc.element = "ticket" AND tc.rowid = ec.fk_c_type_contact';
3807 $sqlprotectagainstexternals .= " WHERE t.ref LIKE '".$db->escape($refname)."'";
3808 $sqlprotectagainstexternals .= ' AND (';
3809 $sqlprotectagainstexternals .= ' (';
3810 $sqlprotectagainstexternals .= ' tc.rowid IS NOT NULL';
3811 $sqlprotectagainstexternals .= " AND c.email = '".$db->escape($email_split[0]).'@'.$db->sanitize($email_split[1])."'";
3812 $sqlprotectagainstexternals .= ' )';
3813 $sqlprotectagainstexternals .= " OR t.origin_email = '".$db->escape($email_split[0]).'@'.$db->sanitize($email_split[1])."'";
3814 $sqlprotectagainstexternals .= ' )';
3815 }
3816 $original_file = $conf->ticket->multidir_output[$entity].'/'.$original_file;
3817 // If modulepart=module_user_temp Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp/iduser
3818 // If modulepart=module_temp Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp
3819 // If modulepart=module_user Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/iduser
3820 // If modulepart=module Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart
3821 // If modulepart=module-abc Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart
3822 } else {
3823 // GENERIC Wrapping
3824 //var_dump($modulepart);
3825 //var_dump($original_file);
3826 if (preg_match('/^specimen/i', $original_file)) {
3827 $accessallowed = 1; // If link to a file called specimen. Test must be done before changing $original_file int full path.
3828 }
3829 if ($fuser->admin) {
3830 $accessallowed = 1; // If user is admin
3831 }
3832
3833 $tmpmodulepart = explode('-', $modulepart);
3834 if (!empty($tmpmodulepart[1])) {
3835 $modulepart = $tmpmodulepart[0];
3836 $original_file = $tmpmodulepart[1].'/'.$original_file;
3837 }
3838
3839 // Define $accessallowed
3840 $reg = array();
3841 if (preg_match('/^([a-z]+)_user_temp$/i', $modulepart, $reg)) {
3842 $tmpmodule = $reg[1];
3843 if (empty($conf->$tmpmodule->dir_temp)) { // modulepart not supported
3844 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3845 exit;
3846 }
3847 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3848 $accessallowed = 1;
3849 }
3850 $original_file = $conf->{$reg[1]}->dir_temp.'/'.$fuser->id.'/'.$original_file;
3851 } elseif (preg_match('/^([a-z]+)_temp$/i', $modulepart, $reg)) {
3852 $tmpmodule = $reg[1];
3853 if (empty($conf->$tmpmodule->dir_temp)) { // modulepart not supported
3854 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3855 exit;
3856 }
3857 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3858 $accessallowed = 1;
3859 }
3860 $original_file = $conf->$tmpmodule->dir_temp.'/'.$original_file;
3861 } elseif (preg_match('/^([a-z]+)_user$/i', $modulepart, $reg)) {
3862 $tmpmodule = $reg[1];
3863 if (empty($conf->$tmpmodule->dir_output)) { // modulepart not supported
3864 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3865 exit;
3866 }
3867 if ($fuser->hasRight($tmpmodule, $lire) || $fuser->hasRight($tmpmodule, $read) || $fuser->hasRight($tmpmodule, $download)) {
3868 $accessallowed = 1;
3869 }
3870 $original_file = $conf->$tmpmodule->dir_output.'/'.$fuser->id.'/'.$original_file;
3871 } elseif (preg_match('/^massfilesarea_([a-z]+)$/i', $modulepart, $reg)) {
3872 $tmpmodule = $reg[1];
3873 if (empty($conf->$tmpmodule->dir_output)) { // modulepart not supported
3874 dol_print_error(null, 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
3875 exit;
3876 }
3877
3878 // Check fuser->rights->modulepart->myobject->read and fuser->rights->modulepart->read
3879 $partsofdirinoriginalfile = explode('/', $original_file);
3880 if (!empty($partsofdirinoriginalfile[1])) { // If original_file is xxx/filename (xxx is a part we will use)
3881 $partofdirinoriginalfile = $partsofdirinoriginalfile[0];
3882 if (($partofdirinoriginalfile && $fuser->hasRight($tmpmodule, $partofdirinoriginalfile, 'read')) || preg_match('/^specimen/i', $original_file)) {
3883 $accessallowed = 1;
3884 }
3885 }
3886 if ($fuser->hasRight($tmpmodule, $read) || preg_match('/^specimen/i', $original_file)) {
3887 $accessallowed = 1;
3888 }
3889 $original_file = $conf->$tmpmodule->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
3890 } else {
3891 if (empty($conf->$modulepart->dir_output)) { // modulepart not supported
3892 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.');
3893 exit;
3894 }
3895
3896 // Check fuser->hasRight('modulepart', 'myobject', 'read') and fuser->hasRight('modulepart', 'read')
3897 $partsofdirinoriginalfile = explode('/', $original_file);
3898 if (!empty($partsofdirinoriginalfile[1])) { // If original_file is xxx/filename (xxx is a part we will use)
3899 $partofdirinoriginalfile = $partsofdirinoriginalfile[0];
3900 if ($partofdirinoriginalfile && ($fuser->hasRight($modulepart, $partofdirinoriginalfile, 'lire') || $fuser->hasRight($modulepart, $partofdirinoriginalfile, 'read'))) {
3901 $accessallowed = 1;
3902 }
3903 }
3904 if (($fuser->hasRight($modulepart, $lire) || $fuser->hasRight($modulepart, $read)) || ($fuser->hasRight($modulepart, 'all', $lire) || $fuser->hasRight($modulepart, 'all', $read))) {
3905 $accessallowed = 1;
3906 }
3907
3908 if (is_array($conf->$modulepart->multidir_output) && !empty($conf->$modulepart->multidir_output[$entity])) {
3909 $original_file = $conf->$modulepart->multidir_output[$entity].'/'.$original_file;
3910 } else {
3911 $original_file = $conf->$modulepart->dir_output.'/'.$original_file;
3912 }
3913 }
3914
3915 $parameters = array(
3916 'modulepart' => $modulepart,
3917 'original_file' => $original_file,
3918 'entity' => $entity,
3919 'fuser' => $fuser,
3920 'refname' => '',
3921 'mode' => $mode
3922 );
3923 $reshook = $hookmanager->executeHooks('checkSecureAccess', $parameters, $object);
3924 if ($reshook > 0) {
3925 if (!empty($hookmanager->resArray['original_file'])) {
3926 $original_file = $hookmanager->resArray['original_file'];
3927 }
3928 if (!empty($hookmanager->resArray['accessallowed'])) {
3929 $accessallowed = $hookmanager->resArray['accessallowed'];
3930 }
3931 if (!empty($hookmanager->resArray['sqlprotectagainstexternals'])) {
3932 $sqlprotectagainstexternals = $hookmanager->resArray['sqlprotectagainstexternals'];
3933 }
3934 }
3935 }
3936
3937 $ret = array(
3938 'accessallowed' => ($accessallowed ? 1 : 0),
3939 'sqlprotectagainstexternals' => $sqlprotectagainstexternals,
3940 'original_file' => $original_file
3941 );
3942
3943 return $ret;
3944}
3945
3954function dol_filecache($directory, $filename, $object)
3955{
3956 if (!dol_is_dir($directory)) {
3957 $result = dol_mkdir($directory);
3958 if ($result < -1) {
3959 dol_syslog("Failed to create the cache directory ".$directory, LOG_WARNING);
3960 }
3961 }
3962 $cachefile = $directory.$filename;
3963
3964 file_put_contents($cachefile, json_encode($object), LOCK_EX);
3965 dolChmod($cachefile);
3966}
3967
3976function dol_cache_refresh($directory, $filename, $cachetime)
3977{
3978 $now = dol_now();
3979 $cachefile = $directory.$filename;
3980 $refresh = !file_exists($cachefile) || ($now - $cachetime) > dol_filemtime($cachefile);
3981 return $refresh;
3982}
3983
3991function dol_readcachefile($directory, $filename)
3992{
3993 $cachefile = $directory.$filename;
3994 $object = json_decode(file_get_contents($cachefile));
3995 return $object;
3996}
3997
4004function dirbasename($pathfile)
4005{
4006 return preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'\//', '', $pathfile);
4007}
4008
4009
4021function getFilesUpdated(&$file_list, SimpleXMLElement $dir, $path = '', $pathref = '', &$checksumconcat = array())
4022{
4023 global $conffile;
4024
4025 //$exclude = 'install';
4026
4027 $entry = array();
4028 $algo = '';
4029 if (!empty($dir->md5file)) {
4030 $entry = $dir->md5file;
4031 $algo = 'md5';
4032 } elseif (!empty($dir->sha256file)) {
4033 $entry = $dir->sha256file;
4034 $algo = 'sha256';
4035 }
4036
4037 foreach ($entry as $file) { // $file is a simpleXMLElement
4038 $filename = $path.$file['name'];
4039 $file_list['insignature'][] = $filename;
4040 $expectedsize = (empty($file['size']) ? '' : $file['size']);
4041 $expectedhash = (string) $file;
4042
4043 if (!file_exists($pathref.'/'.$filename)) {
4044 $file_list['missing'][] = array('filename' => $filename, 'expectedhash' => $expectedhash, 'expectedsize' => $expectedsize, 'algo' => (string) $algo);
4045 } else {
4046 $hash_local = hash_file($algo, $pathref.'/'.$filename);
4047
4048 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
4049 $checksumconcat[] = $expectedhash;
4050 } else {
4051 if ($hash_local != $expectedhash) {
4052 $file_list['updated'][] = array('filename' => $filename, 'expectedhash' => $expectedhash, 'expectedsize' => $expectedsize, 'hash' => (string) $hash_local, 'algo' => (string) $algo);
4053 }
4054 $checksumconcat[] = $hash_local;
4055 }
4056 }
4057 }
4058
4059 foreach ($dir->dir as $subdir) { // $subdir['name'] is '' or '/accountancy/admin' for example
4060 getFilesUpdated($file_list, $subdir, $path.$subdir['name'].'/', $pathref, $checksumconcat);
4061 }
4062
4063 return $file_list;
4064}
4065
4073function dragAndDropFileUpload($htmlname)
4074{
4075 global $object, $langs;
4076
4077 $out = "";
4078 $out .= '<div id="'.$htmlname.'Message" class="dragDropAreaMessage hidden"><span>'.img_picto("", 'download').'<br>'.$langs->trans("DropFileToAddItToObject").'</span></div>';
4079 $out .= "\n<!-- JS CODE TO ENABLE DRAG AND DROP OF FILE -->\n";
4080 $out .= "<script>";
4081 $out .= '
4082 jQuery(document).ready(function() {
4083 var enterTargetDragDrop = null;
4084
4085 $("#'.$htmlname.'").addClass("cssDragDropArea");
4086
4087 $(".cssDragDropArea").on("dragenter", function(ev, ui) {
4088 var dataTransfer = ev.originalEvent.dataTransfer;
4089 var dataTypes = dataTransfer.types;
4090 //console.log(dataTransfer);
4091 //console.log(dataTypes);
4092
4093 if (!dataTypes || ($.inArray(\'Files\', dataTypes) === -1)) {
4094 // The element dragged is not a file, so we avoid the "dragenter"
4095 ev.preventDefault();
4096 return false;
4097 }
4098
4099 // Entering drop area. Highlight area
4100 console.log("dragAndDropFileUpload: We add class highlightDragDropArea")
4101 enterTargetDragDrop = ev.target;
4102 $(this).addClass("highlightDragDropArea");
4103 $("#'.$htmlname.'Message").removeClass("hidden");
4104 ev.preventDefault();
4105 });
4106
4107 $(".cssDragDropArea").on("dragleave", function(ev) {
4108 // Going out of drop area. Remove Highlight
4109 if (enterTargetDragDrop == ev.target){
4110 console.log("dragAndDropFileUpload: We remove class highlightDragDropArea")
4111 $("#'.$htmlname.'Message").addClass("hidden");
4112 $(this).removeClass("highlightDragDropArea");
4113 }
4114 });
4115
4116 $(".cssDragDropArea").on("dragover", function(ev) {
4117 ev.preventDefault();
4118 return false;
4119 });
4120
4121 $(".cssDragDropArea").on("drop", function(e) {
4122 console.log("Trigger event file dropped. fk_element='.dol_escape_js((string) $object->id).' element='.dol_escape_js($object->element).'");
4123 e.preventDefault();
4124 fd = new FormData();
4125 fd.append("fk_element", "'.dol_escape_js((string) $object->id).'");
4126 fd.append("element", "'.dol_escape_js($object->element).'");
4127 fd.append("token", "'.currentToken().'");
4128 fd.append("action", "linkit");
4129
4130 var dataTransfer = e.originalEvent.dataTransfer;
4131
4132 if (dataTransfer.files && dataTransfer.files.length){
4133 var droppedFiles = e.originalEvent.dataTransfer.files;
4134 $.each(droppedFiles, function(index,file){
4135 fd.append("files[]", file,file.name)
4136 });
4137 }
4138 $(".cssDragDropArea").removeClass("highlightDragDropArea");
4139 counterdragdrop = 0;
4140 $.ajax({
4141 url: "'.DOL_URL_ROOT.'/core/ajax/fileupload.php",
4142 type: "POST",
4143 processData: false,
4144 contentType: false,
4145 data: fd,
4146 success:function() {
4147 console.log("Uploaded.", arguments);
4148 /* arguments[0] is the json string of files */
4149 /* arguments[1] is the value for variable "success", can be 0 or 1 */
4150 let listoffiles = JSON.parse(arguments[0]);
4151 console.log(listoffiles);
4152 let nboferror = 0;
4153 for (let i = 0; i < listoffiles.length; i++) {
4154 console.log(listoffiles[i].error);
4155 if (listoffiles[i].error) {
4156 nboferror++;
4157 }
4158 }
4159 console.log(nboferror);
4160 if (nboferror > 0) {
4161 window.location.href = "'.$_SERVER["PHP_SELF"].'?id='.dol_escape_js((string) $object->id).'&seteventmessages=ErrorOnAtLeastOneFileUpload:warnings";
4162 } else {
4163 window.location.href = "'.$_SERVER["PHP_SELF"].'?id='.dol_escape_js((string) $object->id).'&seteventmessages=UploadFileDragDropSuccess:mesgs";
4164 }
4165 },
4166 error:function() {
4167 console.log("Error Uploading.", arguments)
4168 if (arguments[0].status == 403) {
4169 window.location.href = "'.$_SERVER["PHP_SELF"].'?id='.dol_escape_js((string) $object->id).'&seteventmessages=ErrorUploadPermissionDenied:errors";
4170 }
4171 window.location.href = "'.$_SERVER["PHP_SELF"].'?id='.dol_escape_js((string) $object->id).'&seteventmessages=ErrorUploadFileDragDropPermissionDenied:errors";
4172 },
4173 })
4174 });
4175 });
4176 ';
4177 $out .= "</script>\n";
4178 return $out;
4179}
4180
4191function archiveOrBackupFile($srcfile, $max_versions = 5, $archivedir = '', $suffix = "v", $moveorcopy = 'move')
4192{
4193 $base_file_pattern = ($archivedir ? $archivedir : dirname($srcfile)).'/'.basename($srcfile).".".$suffix;
4194 $files_in_directory = glob($base_file_pattern . "*");
4195
4196 // Extract the modification timestamps for each file
4197 $files_with_timestamps = [];
4198 foreach ($files_in_directory as $file) {
4199 $files_with_timestamps[] = [
4200 'file' => $file,
4201 'timestamp' => filemtime($file)
4202 ];
4203 }
4204
4205 // Sort the files by modification date
4206 $sorted_files = [];
4207 while (count($files_with_timestamps) > 0) {
4208 $latest_file = null;
4209 $latest_index = null;
4210
4211 // Find the latest file by timestamp
4212 foreach ($files_with_timestamps as $index => $file_info) {
4213 if ($latest_file === null || (is_array($latest_file) && $file_info['timestamp'] > $latest_file['timestamp'])) {
4214 $latest_file = $file_info;
4215 $latest_index = $index;
4216 }
4217 }
4218
4219 // Add the latest file to the sorted list and remove it from the original list
4220 if ($latest_file !== null) {
4221 $sorted_files[] = $latest_file['file'];
4222 unset($files_with_timestamps[$latest_index]);
4223 }
4224 }
4225
4226 // Delete the oldest files to keep only the allowed number of versions
4227 if (count($sorted_files) >= $max_versions) {
4228 $oldest_files = array_slice($sorted_files, $max_versions - 1);
4229 foreach ($oldest_files as $oldest_file) {
4230 dol_delete_file($oldest_file, 0, 0, 0, null, false, 0);
4231 }
4232 }
4233
4234 $timestamp = dol_now('gmt');
4235 $new_backup = $srcfile . ".v" . $timestamp;
4236
4237 // Move or copy the original file to the new backup with the timestamp
4238 if ($moveorcopy == 'move') {
4239 $result = dol_move($srcfile, $new_backup, '0', 1, 0, 0);
4240 } else {
4241 $result = dol_copy($srcfile, $new_backup, '0', 1, 0, 0);
4242 }
4243
4244 if (!$result) {
4245 return false;
4246 }
4247
4248 return true;
4249}
4250
4257function dolDocToText($filetoprocess, $useFullTextIndexation = 'pdftotext', $options = 'html')
4258{
4259 global $conf, $db, $user;
4260
4261 $error = 0;
4262 $keywords = array();
4263 $textforfulltextindex = '';
4264 $cmd = '';
4265
4266 if (empty($useFullTextIndexation)) {
4267 $useFullTextIndexation = 'pdftotext';
4268 }
4269
4270 // TODO Move this into external submodule files
4271
4272 // TODO Develop a native PHP parser using sample code in https://github.com/adeel/php-pdf-parser or https://github.com/smalot/pdfparser
4273 // Use the method pdftotext to generate a HTML
4274 if (preg_match('/pdftotext/i', $useFullTextIndexation)) {
4275 include_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
4276 $utils = new Utils($db);
4277 $outputfile = $conf->admin->dir_temp.'/tmppdftotext.'.$user->id.'.out'; // File used with popen method
4278
4279 // We also exclude '/temp/' dir and 'documents/admin/documents'
4280 // We make escapement here and call executeCLI without escapement because we don't want to have the '*.log' escaped.
4281 if ($options == 'fulltext') {
4282 $params = '-nodiag -layout';
4283 } else {
4284 $params = '-htmlmeta';
4285 }
4286
4287 // MAIN_SAVE_FILE_CONTENT_AS_TEXT_PDFTOTEXT can be for example: "/usr/bin/pdftotext"
4288 $cmd = escapeshellcmd(dol_sanitizePathName(getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_PDFTOTEXT', 'pdftotext'))) . " " . $params ." '".escapeshellcmd($filetoprocess)."' - ";
4289 $resultexec = $utils->executeCLI($cmd, $outputfile, 0, null, 1);
4290
4291 if (empty($resultexec['error'])) {
4292 $matches = array();
4293 if ($options == 'fulltext') {
4294 $textforfulltextindex = $resultexec['output'];
4295 }
4296 if ($options == 'html') {
4297 $txt = $resultexec['output'];
4298 if (preg_match('/<meta name="keywords" content="([^\/]+)"\s*\/>/i', $txt, $matches)) {
4299 $keywords = $matches[1];
4300 }
4301 if (preg_match('/<pre>(.*)<\/pre>/si', $txt, $matches)) {
4302 $textforfulltextindex = dol_string_nounprintableascii($matches[1], 0);
4303 }
4304 }
4305 } else {
4306 dol_syslog($resultexec['error']);
4307 $error++;
4308 }
4309 }
4310
4311
4312 // Use the method docling to generate a .md (https://ds4sd.github.io/docling/)
4313 if (preg_match('/docling/i', $useFullTextIndexation)) {
4314 include_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
4315 $utils = new Utils($db);
4316 $outputfile = $conf->admin->dir_temp.'/tmpdocling.'.$user->id.'.out'; // File used with popen method
4317
4318 // We also exclude '/temp/' dir and 'documents/admin/documents'
4319 // We make escapement here and call executeCLI without escapement because we don't want to have the '*.log' escaped.
4320 // MAIN_SAVE_FILE_CONTENT_AS_TEXT_DOCLING can be for example: "/usr/bin/docling"
4321 $cmd = escapeshellcmd(dol_sanitizePathName(getDolGlobalString('MAIN_SAVE_FILE_CONTENT_AS_TEXT_DOCLING', 'docling')))." --from pdf --to text '".escapeshellcmd($filetoprocess)."'";
4322 $resultexec = $utils->executeCLI($cmd, $outputfile, 0, null, 1);
4323
4324 if (!$resultexec['error']) {
4325 $txt = $resultexec['output'];
4326 //$matches = array();
4327 //if (preg_match('/<meta name="Keywords" content="([^\/]+)"\s*\/>/i', $txt, $matches)) {
4328 // $keywords = $matches[1];
4329 //}
4330 //if (preg_match('/<pre>(.*)<\/pre>/si', $txt, $matches)) {
4331 // $textforfulltextindex = dol_string_nounprintableascii($matches[1], 0);
4332 //}
4333 $textforfulltextindex = $txt;
4334 } else {
4335 dol_syslog($resultexec['error']);
4336 $error++;
4337 }
4338 }
4339
4340 return array('error' => $error, 'keywords' => $keywords, 'content' => $textforfulltextindex, 'cmd' => $cmd);
4341}
4342
4349function removeLastLine($fullpath)
4350{
4351 // Generate tmp file content without the last line
4352 $fp = fopen($fullpath, "r");
4353 fseek($fp, -1, SEEK_END);
4354 $pos = -1;
4355 $char = fgetc($fp);
4356 while ($char === "\n" || $char === "\r") { // Go to last real char of last line
4357 fseek($fp, $pos--, SEEK_END);
4358 $char = fgetc($fp);
4359 }
4360 while ($char !== "\n" && $char !== false) {
4361 fseek($fp, $pos--, SEEK_END);
4362 $char = fgetc($fp);
4363 }
4364 /*
4365 while ($char === "\n" || $char === "\r") { // Go to last real char of last-1 line
4366 fseek($fp, $pos--, SEEK_END);
4367 $char = fgetc($fp);
4368 }
4369 */
4370 $truncatePos = ftell($fp);
4371 fclose($fp);
4372 // Truncate the tmp file to remove the last line
4373 $fp = fopen($fullpath, "c+");
4374 ftruncate($fp, $truncatePos);
4375 fclose($fp);
4376
4377 return 1;
4378}
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.