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