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