1<?php 2/* Copyright (C) 2008-2012 Laurent Destailleur <eldy@users.sourceforge.net> 3 * Copyright (C) 2012-2015 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 Frédéric France <frederic.france@netlogic.fr> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 3 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program. If not, see <https://www.gnu.org/licenses/>. 21 * or see https://www.gnu.org/ 22 */ 23 24/** 25 * \file htdocs/core/lib/files.lib.php 26 * \brief Library for file managing functions 27 */ 28 29/** 30 * Make a basename working with all page code (default PHP basenamed fails with cyrillic). 31 * We supose dir separator for input is '/'. 32 * 33 * @param string $pathfile String to find basename. 34 * @return string Basename of input 35 */ 36function dol_basename($pathfile) 37{ 38 return preg_replace('/^.*\/([^\/]+)$/', '$1', rtrim($pathfile, '/')); 39} 40 41/** 42 * Scan a directory and return a list of files/directories. 43 * Content for string is UTF8 and dir separator is "/". 44 * 45 * @param string $path Starting path from which to search. This is a full path. 46 * @param string $types Can be "directories", "files", or "all" 47 * @param int $recursive Determines whether subdirectories are searched 48 * @param string $filter Regex filter to restrict list. This regex value must be escaped for '/' by doing preg_quote($var,'/'), since this char is used for preg_match function, 49 * but must not contains the start and end '/'. Filter is checked into basename only. 50 * @param array $excludefilter Array of Regex for exclude filter (example: array('(\.meta|_preview.*\.png)$','^\.')). Exclude is checked both into fullpath and into basename (So '^xxx' may exclude 'xxx/dirscanned/...' and dirscanned/xxx'). 51 * @param string $sortcriteria Sort criteria ('','fullname','relativename','name','date','size') 52 * @param string $sortorder Sort order (SORT_ASC, SORT_DESC) 53 * @param int $mode 0=Return array minimum keys loaded (faster), 1=Force all keys like date and size to be loaded (slower), 2=Force load of date only, 3=Force load of size only 54 * @param int $nohook Disable all hooks 55 * @param string $relativename For recursive purpose only. Must be "" at first call. 56 * @param string $donotfollowsymlinks Do not follow symbolic links 57 * @return array Array of array('name'=>'xxx','fullname'=>'/abc/xxx','date'=>'yyy','size'=>99,'type'=>'dir|file',...) 58 * @see dol_dir_list_in_database() 59 */ 60function dol_dir_list($path, $types = "all", $recursive = 0, $filter = "", $excludefilter = null, $sortcriteria = "name", $sortorder = SORT_ASC, $mode = 0, $nohook = 0, $relativename = "", $donotfollowsymlinks = 0) 61{ 62 global $db, $hookmanager; 63 global $object; 64 65 dol_syslog("files.lib.php::dol_dir_list path=".$path." types=".$types." recursive=".$recursive." filter=".$filter." excludefilter=".json_encode($excludefilter)); 66 //print 'xxx'."files.lib.php::dol_dir_list path=".$path." types=".$types." recursive=".$recursive." filter=".$filter." excludefilter=".json_encode($excludefilter); 67 68 $loaddate = ($mode == 1 || $mode == 2) ?true:false; 69 $loadsize = ($mode == 1 || $mode == 3) ?true:false; 70 71 // Clean parameters 72 $path = preg_replace('/([\\/]+)$/i', '', $path); 73 $newpath = dol_osencode($path); 74 75 $reshook = 0; 76 $file_list = array(); 77 78 if (is_object($hookmanager) && !$nohook) 79 { 80 $hookmanager->resArray = array(); 81 82 $hookmanager->initHooks(array('fileslib')); 83 84 $parameters = array( 85 'path' => $newpath, 86 'types'=> $types, 87 'recursive' => $recursive, 88 'filter' => $filter, 89 'excludefilter' => $excludefilter, 90 'sortcriteria' => $sortcriteria, 91 'sortorder' => $sortorder, 92 'loaddate' => $loaddate, 93 'loadsize' => $loadsize, 94 'mode' => $mode 95 ); 96 $reshook = $hookmanager->executeHooks('getDirList', $parameters, $object); 97 } 98 99 // $hookmanager->resArray may contain array stacked by other modules 100 if (empty($reshook)) 101 { 102 if (!is_dir($newpath)) return array(); 103 104 if ($dir = opendir($newpath)) 105 { 106 $filedate = ''; 107 $filesize = ''; 108 109 while (false !== ($file = readdir($dir))) // $file is always a basename (into directory $newpath) 110 { 111 if (!utf8_check($file)) $file = utf8_encode($file); // To be sure data is stored in utf8 in memory 112 $fullpathfile = ($newpath ? $newpath.'/' : '').$file; 113 114 $qualified = 1; 115 116 // Define excludefilterarray 117 $excludefilterarray = array('^\.'); 118 if (is_array($excludefilter)) 119 { 120 $excludefilterarray = array_merge($excludefilterarray, $excludefilter); 121 } elseif ($excludefilter) $excludefilterarray[] = $excludefilter; 122 // Check if file is qualified 123 foreach ($excludefilterarray as $filt) 124 { 125 if (preg_match('/'.$filt.'/i', $file) || preg_match('/'.$filt.'/i', $fullpathfile)) { 126 $qualified = 0; break; 127 } 128 } 129 //print $fullpathfile.' '.$file.' '.$qualified.'<br>'; 130 131 if ($qualified) 132 { 133 $isdir = is_dir(dol_osencode($path."/".$file)); 134 // Check whether this is a file or directory and whether we're interested in that type 135 if ($isdir && (($types == "directories") || ($types == "all") || $recursive)) 136 { 137 // Add entry into file_list array 138 if (($types == "directories") || ($types == "all")) 139 { 140 if ($loaddate || $sortcriteria == 'date') $filedate = dol_filemtime($path."/".$file); 141 if ($loadsize || $sortcriteria == 'size') $filesize = dol_filesize($path."/".$file); 142 143 if (!$filter || preg_match('/'.$filter.'/i', $file)) // We do not search key $filter into all $path, only into $file part 144 { 145 $reg = array(); 146 preg_match('/([^\/]+)\/[^\/]+$/', $path.'/'.$file, $reg); 147 $level1name = (isset($reg[1]) ? $reg[1] : ''); 148 $file_list[] = array( 149 "name" => $file, 150 "path" => $path, 151 "level1name" => $level1name, 152 "relativename" => ($relativename ? $relativename.'/' : '').$file, 153 "fullname" => $path.'/'.$file, 154 "date" => $filedate, 155 "size" => $filesize, 156 "type" => 'dir' 157 ); 158 } 159 } 160 161 // if we're in a directory and we want recursive behavior, call this function again 162 if ($recursive) 163 { 164 if (empty($donotfollowsymlinks) || !is_link($path."/".$file)) 165 { 166 //var_dump('eee '. $path."/".$file. ' '.is_dir($path."/".$file).' '.is_link($path."/".$file)); 167 $file_list = array_merge($file_list, dol_dir_list($path."/".$file, $types, $recursive, $filter, $excludefilter, $sortcriteria, $sortorder, $mode, $nohook, ($relativename != '' ? $relativename.'/' : '').$file, $donotfollowsymlinks)); 168 } 169 } 170 } elseif (!$isdir && (($types == "files") || ($types == "all"))) 171 { 172 // Add file into file_list array 173 if ($loaddate || $sortcriteria == 'date') $filedate = dol_filemtime($path."/".$file); 174 if ($loadsize || $sortcriteria == 'size') $filesize = dol_filesize($path."/".$file); 175 176 if (!$filter || preg_match('/'.$filter.'/i', $file)) // We do not search key $filter into $path, only into $file 177 { 178 preg_match('/([^\/]+)\/[^\/]+$/', $path.'/'.$file, $reg); 179 $level1name = (isset($reg[1]) ? $reg[1] : ''); 180 $file_list[] = array( 181 "name" => $file, 182 "path" => $path, 183 "level1name" => $level1name, 184 "relativename" => ($relativename ? $relativename.'/' : '').$file, 185 "fullname" => $path.'/'.$file, 186 "date" => $filedate, 187 "size" => $filesize, 188 "type" => 'file' 189 ); 190 } 191 } 192 } 193 } 194 closedir($dir); 195 196 // Obtain a list of columns 197 if (!empty($sortcriteria) && $sortorder) 198 { 199 $file_list = dol_sort_array($file_list, $sortcriteria, ($sortorder == SORT_ASC ? 'asc' : 'desc')); 200 } 201 } 202 } 203 204 if (is_object($hookmanager) && is_array($hookmanager->resArray)) $file_list = array_merge($file_list, $hookmanager->resArray); 205 206 return $file_list; 207} 208 209 210/** 211 * Scan a directory and return a list of files/directories. 212 * Content for string is UTF8 and dir separator is "/". 213 * 214 * @param string $path Starting path from which to search. Example: 'produit/MYPROD' 215 * @param string $filter Regex filter to restrict list. This regex value must be escaped for '/', since this char is used for preg_match function 216 * @param array|null $excludefilter Array of Regex for exclude filter (example: array('(\.meta|_preview.*\.png)$','^\.')) 217 * @param string $sortcriteria Sort criteria ("","fullname","name","date","size") 218 * @param string $sortorder Sort order (SORT_ASC, SORT_DESC) 219 * @param int $mode 0=Return array minimum keys loaded (faster), 1=Force all keys like description 220 * @return array Array of array('name'=>'xxx','fullname'=>'/abc/xxx','type'=>'dir|file',...) 221 * @see dol_dir_list() 222 */ 223function dol_dir_list_in_database($path, $filter = "", $excludefilter = null, $sortcriteria = "name", $sortorder = SORT_ASC, $mode = 0) 224{ 225 global $conf, $db; 226 227 $sql = " SELECT rowid, label, entity, filename, filepath, fullpath_orig, keywords, cover, gen_or_uploaded, extraparams,"; 228 $sql .= " date_c, tms as date_m, fk_user_c, fk_user_m, acl, position, share"; 229 if ($mode) $sql .= ", description"; 230 $sql .= " FROM ".MAIN_DB_PREFIX."ecm_files"; 231 $sql .= " WHERE entity = ".$conf->entity; 232 if (preg_match('/%$/', $path)) { 233 $sql .= " AND filepath LIKE '".$db->escape($path)."'"; 234 } else { 235 $sql .= " AND filepath = '".$db->escape($path)."'"; 236 } 237 238 $resql = $db->query($sql); 239 if ($resql) 240 { 241 $file_list = array(); 242 $num = $db->num_rows($resql); 243 $i = 0; 244 while ($i < $num) 245 { 246 $obj = $db->fetch_object($resql); 247 if ($obj) 248 { 249 $reg = array(); 250 preg_match('/([^\/]+)\/[^\/]+$/', DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename, $reg); 251 $level1name = (isset($reg[1]) ? $reg[1] : ''); 252 $file_list[] = array( 253 "rowid" => $obj->rowid, 254 "label" => $obj->label, // md5 255 "name" => $obj->filename, 256 "path" => DOL_DATA_ROOT.'/'.$obj->filepath, 257 "level1name" => $level1name, 258 "fullname" => DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename, 259 "fullpath_orig" => $obj->fullpath_orig, 260 "date_c" => $db->jdate($obj->date_c), 261 "date_m" => $db->jdate($obj->date_m), 262 "type" => 'file', 263 "keywords" => $obj->keywords, 264 "cover" => $obj->cover, 265 "position" => (int) $obj->position, 266 "acl" => $obj->acl, 267 "share" => $obj->share 268 ); 269 } 270 $i++; 271 } 272 273 // Obtain a list of columns 274 if (!empty($sortcriteria)) 275 { 276 $myarray = array(); 277 foreach ($file_list as $key => $row) 278 { 279 $myarray[$key] = (isset($row[$sortcriteria]) ? $row[$sortcriteria] : ''); 280 } 281 // Sort the data 282 if ($sortorder) array_multisort($myarray, $sortorder, $file_list); 283 } 284 285 return $file_list; 286 } else { 287 dol_print_error($db); 288 return array(); 289 } 290} 291 292 293/** 294 * Complete $filearray with data from database. 295 * This will call doldir_list_indatabase to complate filearray. 296 * 297 * @param array $filearray Array of files obtained using dol_dir_list 298 * @param string $relativedir Relative dir from DOL_DATA_ROOT 299 * @return void 300 */ 301function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir) 302{ 303 global $conf, $db, $user; 304 305 $filearrayindatabase = dol_dir_list_in_database($relativedir, '', null, 'name', SORT_ASC); 306 307 // TODO Remove this when PRODUCT_USE_OLD_PATH_FOR_PHOTO will be removed 308 global $modulepart; 309 if ($modulepart == 'produit' && !empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { 310 global $object; 311 if (!empty($object->id)) 312 { 313 if (!empty($conf->product->enabled)) $upload_dirold = $conf->product->multidir_output[$object->entity].'/'.substr(substr("000".$object->id, -2), 1, 1).'/'.substr(substr("000".$object->id, -2), 0, 1).'/'.$object->id."/photos"; 314 else $upload_dirold = $conf->service->multidir_output[$object->entity].'/'.substr(substr("000".$object->id, -2), 1, 1).'/'.substr(substr("000".$object->id, -2), 0, 1).'/'.$object->id."/photos"; 315 316 $relativedirold = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dirold); 317 $relativedirold = preg_replace('/^[\\/]/', '', $relativedirold); 318 319 $filearrayindatabase = array_merge($filearrayindatabase, dol_dir_list_in_database($relativedirold, '', null, 'name', SORT_ASC)); 320 } 321 } 322 323 /*var_dump($relativedir); 324 var_dump($filearray); 325 var_dump($filearrayindatabase);*/ 326 327 // Complete filearray with properties found into $filearrayindatabase 328 foreach ($filearray as $key => $val) 329 { 330 $tmpfilename = preg_replace('/\.noexe$/', '', $filearray[$key]['name']); 331 $found = 0; 332 // Search if it exists into $filearrayindatabase 333 foreach ($filearrayindatabase as $key2 => $val2) 334 { 335 if (($filearrayindatabase[$key2]['path'] == $filearray[$key]['path']) && ($filearrayindatabase[$key2]['name'] == $tmpfilename)) 336 { 337 $filearray[$key]['position_name'] = ($filearrayindatabase[$key2]['position'] ? $filearrayindatabase[$key2]['position'] : '0').'_'.$filearrayindatabase[$key2]['name']; 338 $filearray[$key]['position'] = $filearrayindatabase[$key2]['position']; 339 $filearray[$key]['cover'] = $filearrayindatabase[$key2]['cover']; 340 $filearray[$key]['acl'] = $filearrayindatabase[$key2]['acl']; 341 $filearray[$key]['rowid'] = $filearrayindatabase[$key2]['rowid']; 342 $filearray[$key]['label'] = $filearrayindatabase[$key2]['label']; 343 $filearray[$key]['share'] = $filearrayindatabase[$key2]['share']; 344 $found = 1; 345 break; 346 } 347 } 348 349 if (!$found) // This happen in transition toward version 6, or if files were added manually into os dir. 350 { 351 $filearray[$key]['position'] = '999999'; // File not indexed are at end. So if we add a file, it will not replace an existing position 352 $filearray[$key]['cover'] = 0; 353 $filearray[$key]['acl'] = ''; 354 355 $rel_filename = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filearray[$key]['fullname']); 356 357 if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filename)) // If not a tmp file 358 { 359 dol_syslog("list_of_documents We found a file called '".$filearray[$key]['name']."' not indexed into database. We add it"); 360 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; 361 $ecmfile = new EcmFiles($db); 362 363 // Add entry into database 364 $filename = basename($rel_filename); 365 $rel_dir = dirname($rel_filename); 366 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); 367 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); 368 369 $ecmfile->filepath = $rel_dir; 370 $ecmfile->filename = $filename; 371 $ecmfile->label = md5_file(dol_osencode($filearray[$key]['fullname'])); // $destfile is a full path to file 372 $ecmfile->fullpath_orig = $filearray[$key]['fullname']; 373 $ecmfile->gen_or_uploaded = 'unknown'; 374 $ecmfile->description = ''; // indexed content 375 $ecmfile->keyword = ''; // keyword content 376 $result = $ecmfile->create($user); 377 if ($result < 0) 378 { 379 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings'); 380 } else { 381 $filearray[$key]['rowid'] = $result; 382 } 383 } else { 384 $filearray[$key]['rowid'] = 0; // Should not happened 385 } 386 } 387 } 388 //var_dump($filearray); var_dump($relativedir.' - tmpfilename='.$tmpfilename.' - found='.$found); 389} 390 391 392/** 393 * Fast compare of 2 files identified by their properties ->name, ->date and ->size 394 * 395 * @param string $a File 1 396 * @param string $b File 2 397 * @return int 1, 0, 1 398 */ 399function dol_compare_file($a, $b) 400{ 401 global $sortorder; 402 global $sortfield; 403 404 $sortorder = strtoupper($sortorder); 405 406 if ($sortorder == 'ASC') { $retup = -1; $retdown = 1; } else { $retup = 1; $retdown = -1; } 407 408 if ($sortfield == 'name') 409 { 410 if ($a->name == $b->name) return 0; 411 return ($a->name < $b->name) ? $retup : $retdown; 412 } 413 if ($sortfield == 'date') 414 { 415 if ($a->date == $b->date) return 0; 416 return ($a->date < $b->date) ? $retup : $retdown; 417 } 418 if ($sortfield == 'size') 419 { 420 if ($a->size == $b->size) return 0; 421 return ($a->size < $b->size) ? $retup : $retdown; 422 } 423} 424 425 426/** 427 * Test if filename is a directory 428 * 429 * @param string $folder Name of folder 430 * @return boolean True if it's a directory, False if not found 431 */ 432function dol_is_dir($folder) 433{ 434 $newfolder = dol_osencode($folder); 435 if (is_dir($newfolder)) return true; 436 else return false; 437} 438 439/** 440 * Return if path is empty 441 * 442 * @param string $dir Path of Directory 443 * @return boolean True or false 444 */ 445function dol_is_dir_empty($dir) 446{ 447 if (!is_readable($dir)) return false; 448 return (count(scandir($dir)) == 2); 449} 450 451/** 452 * Return if path is a file 453 * 454 * @param string $pathoffile Path of file 455 * @return boolean True or false 456 */ 457function dol_is_file($pathoffile) 458{ 459 $newpathoffile = dol_osencode($pathoffile); 460 return is_file($newpathoffile); 461} 462 463/** 464 * Return if path is a symbolic link 465 * 466 * @param string $pathoffile Path of file 467 * @return boolean True or false 468 */ 469function dol_is_link($pathoffile) 470{ 471 $newpathoffile = dol_osencode($pathoffile); 472 return is_link($newpathoffile); 473} 474 475/** 476 * Return if path is an URL 477 * 478 * @param string $url Url 479 * @return boolean True or false 480 */ 481function dol_is_url($url) 482{ 483 $tmpprot = array('file', 'http', 'https', 'ftp', 'zlib', 'data', 'ssh', 'ssh2', 'ogg', 'expect'); 484 foreach ($tmpprot as $prot) 485 { 486 if (preg_match('/^'.$prot.':/i', $url)) return true; 487 } 488 return false; 489} 490 491/** 492 * Test if a folder is empty 493 * 494 * @param string $folder Name of folder 495 * @return boolean True if dir is empty or non-existing, False if it contains files 496 */ 497function dol_dir_is_emtpy($folder) 498{ 499 $newfolder = dol_osencode($folder); 500 if (is_dir($newfolder)) 501 { 502 $handle = opendir($newfolder); 503 $folder_content = ''; 504 while ((gettype($name = readdir($handle)) != "boolean")) 505 { 506 $name_array[] = $name; 507 } 508 foreach ($name_array as $temp) $folder_content .= $temp; 509 510 closedir($handle); 511 512 if ($folder_content == "...") return true; 513 else return false; 514 } else return true; // Dir does not exists 515} 516 517/** 518 * Count number of lines in a file 519 * 520 * @param string $file Filename 521 * @return int <0 if KO, Number of lines in files if OK 522 * @see dol_nboflines() 523 */ 524function dol_count_nb_of_line($file) 525{ 526 $nb = 0; 527 528 $newfile = dol_osencode($file); 529 //print 'x'.$file; 530 $fp = fopen($newfile, 'r'); 531 if ($fp) 532 { 533 while (!feof($fp)) 534 { 535 $line = fgets($fp); 536 // We increase count only if read was success. We need test because feof return true only after fgets so we do n+1 fgets for a file with n lines. 537 if (!$line === false) $nb++; 538 } 539 fclose($fp); 540 } else { 541 $nb = -1; 542 } 543 544 return $nb; 545} 546 547 548/** 549 * Return size of a file 550 * 551 * @param string $pathoffile Path of file 552 * @return integer File size 553 * @see dol_print_size() 554 */ 555function dol_filesize($pathoffile) 556{ 557 $newpathoffile = dol_osencode($pathoffile); 558 return filesize($newpathoffile); 559} 560 561/** 562 * Return time of a file 563 * 564 * @param string $pathoffile Path of file 565 * @return int Time of file 566 */ 567function dol_filemtime($pathoffile) 568{ 569 $newpathoffile = dol_osencode($pathoffile); 570 return @filemtime($newpathoffile); // @Is to avoid errors if files does not exists 571} 572 573/** 574 * Make replacement of strings into a file. 575 * 576 * @param string $srcfile Source file (can't be a directory) 577 * @param array $arrayreplacement Array with strings to replace. Example: array('valuebefore'=>'valueafter', ...) 578 * @param string $destfile Destination file (can't be a directory). If empty, will be same than source file. 579 * @param int $newmask Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666' 580 * @param int $indexdatabase 1=index new file into database. 581 * @param int $arrayreplacementisregex 1=Array of replacement is regex 582 * @return int <0 if error, 0 if nothing done (dest file already exists), >0 if OK 583 * @see dol_copy() 584 */ 585function dolReplaceInFile($srcfile, $arrayreplacement, $destfile = '', $newmask = 0, $indexdatabase = 0, $arrayreplacementisregex = 0) 586{ 587 global $conf; 588 589 dol_syslog("files.lib.php::dolReplaceInFile srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." indexdatabase=".$indexdatabase." arrayreplacementisregex=".$arrayreplacementisregex); 590 591 if (empty($srcfile)) return -1; 592 if (empty($destfile)) $destfile = $srcfile; 593 594 $destexists = dol_is_file($destfile); 595 if (($destfile != $srcfile) && $destexists) return 0; 596 597 $tmpdestfile = $destfile.'.tmp'; 598 599 $newpathofsrcfile = dol_osencode($srcfile); 600 $newpathoftmpdestfile = dol_osencode($tmpdestfile); 601 $newpathofdestfile = dol_osencode($destfile); 602 $newdirdestfile = dirname($newpathofdestfile); 603 604 if ($destexists && !is_writable($newpathofdestfile)) 605 { 606 dol_syslog("files.lib.php::dolReplaceInFile failed Permission denied to overwrite target file", LOG_WARNING); 607 return -1; 608 } 609 if (!is_writable($newdirdestfile)) 610 { 611 dol_syslog("files.lib.php::dolReplaceInFile failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING); 612 return -2; 613 } 614 615 dol_delete_file($tmpdestfile); 616 617 // Create $newpathoftmpdestfile from $newpathofsrcfile 618 $content = file_get_contents($newpathofsrcfile, 'r'); 619 620 if (empty($arrayreplacementisregex)) 621 { 622 $content = make_substitutions($content, $arrayreplacement, null); 623 } else { 624 foreach ($arrayreplacement as $key => $value) 625 { 626 $content = preg_replace($key, $value, $content); 627 } 628 } 629 630 file_put_contents($newpathoftmpdestfile, $content); 631 @chmod($newpathoftmpdestfile, octdec($newmask)); 632 633 // Rename 634 $result = dol_move($newpathoftmpdestfile, $newpathofdestfile, $newmask, (($destfile == $srcfile) ? 1 : 0), 0, $indexdatabase); 635 if (!$result) 636 { 637 dol_syslog("files.lib.php::dolReplaceInFile failed to move tmp file to final dest", LOG_WARNING); 638 return -3; 639 } 640 if (empty($newmask) && !empty($conf->global->MAIN_UMASK)) $newmask = $conf->global->MAIN_UMASK; 641 if (empty($newmask)) // This should no happen 642 { 643 dol_syslog("Warning: dolReplaceInFile called with empty value for newmask and no default value defined", LOG_WARNING); 644 $newmask = '0664'; 645 } 646 647 @chmod($newpathofdestfile, octdec($newmask)); 648 649 return 1; 650} 651 652 653/** 654 * Copy a file to another file. 655 * 656 * @param string $srcfile Source file (can't be a directory) 657 * @param string $destfile Destination file (can't be a directory) 658 * @param int $newmask Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666' 659 * @param int $overwriteifexists Overwrite file if exists (1 by default) 660 * @return int <0 if error, 0 if nothing done (dest file already exists and overwriteifexists=0), >0 if OK 661 * @see dol_delete_file() dolCopyDir() 662 */ 663function dol_copy($srcfile, $destfile, $newmask = 0, $overwriteifexists = 1) 664{ 665 global $conf; 666 667 dol_syslog("files.lib.php::dol_copy srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists); 668 669 if (empty($srcfile) || empty($destfile)) return -1; 670 671 $destexists = dol_is_file($destfile); 672 if (!$overwriteifexists && $destexists) return 0; 673 674 $newpathofsrcfile = dol_osencode($srcfile); 675 $newpathofdestfile = dol_osencode($destfile); 676 $newdirdestfile = dirname($newpathofdestfile); 677 678 if ($destexists && !is_writable($newpathofdestfile)) 679 { 680 dol_syslog("files.lib.php::dol_copy failed Permission denied to overwrite target file", LOG_WARNING); 681 return -1; 682 } 683 if (!is_writable($newdirdestfile)) 684 { 685 dol_syslog("files.lib.php::dol_copy failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING); 686 return -2; 687 } 688 // Copy with overwriting if exists 689 $result = @copy($newpathofsrcfile, $newpathofdestfile); 690 //$result=copy($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @ 691 if (!$result) 692 { 693 dol_syslog("files.lib.php::dol_copy failed to copy", LOG_WARNING); 694 return -3; 695 } 696 if (empty($newmask) && !empty($conf->global->MAIN_UMASK)) $newmask = $conf->global->MAIN_UMASK; 697 if (empty($newmask)) // This should no happen 698 { 699 dol_syslog("Warning: dol_copy called with empty value for newmask and no default value defined", LOG_WARNING); 700 $newmask = '0664'; 701 } 702 703 @chmod($newpathofdestfile, octdec($newmask)); 704 705 return 1; 706} 707 708/** 709 * Copy a dir to another dir. This include recursive subdirectories. 710 * 711 * @param string $srcfile Source file (a directory) 712 * @param string $destfile Destination file (a directory) 713 * @param int $newmask Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666' 714 * @param int $overwriteifexists Overwrite file if exists (1 by default) 715 * @param array $arrayreplacement Array to use to replace filenames with another one during the copy (works only on file names, not on directory names). 716 * @param int $excludesubdir 0=Do not exclude subdirectories, 1=Exclude subdirectories, 2=Exclude subdirectories if name is not a 2 chars (used for country codes subdirectories). 717 * @return int <0 if error, 0 if nothing done (all files already exists and overwriteifexists=0), >0 if OK 718 * @see dol_copy() 719 */ 720function dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists, $arrayreplacement = null, $excludesubdir = 0) 721{ 722 global $conf; 723 724 $result = 0; 725 726 dol_syslog("files.lib.php::dolCopyDir srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists); 727 728 if (empty($srcfile) || empty($destfile)) return -1; 729 730 $destexists = dol_is_dir($destfile); 731 //if (! $overwriteifexists && $destexists) return 0; // The overwriteifexists is for files only, so propagated to dol_copy only. 732 733 if (!$destexists) 734 { 735 // We must set mask just before creating dir, becaause it can be set differently by dol_copy 736 umask(0); 737 $dirmaskdec = octdec($newmask); 738 if (empty($newmask) && !empty($conf->global->MAIN_UMASK)) $dirmaskdec = octdec($conf->global->MAIN_UMASK); 739 $dirmaskdec |= octdec('0200'); // Set w bit required to be able to create content for recursive subdirs files 740 dol_mkdir($destfile, '', decoct($dirmaskdec)); 741 } 742 743 $ossrcfile = dol_osencode($srcfile); 744 $osdestfile = dol_osencode($destfile); 745 746 // Recursive function to copy all subdirectories and contents: 747 if (is_dir($ossrcfile)) 748 { 749 $dir_handle = opendir($ossrcfile); 750 while ($file = readdir($dir_handle)) 751 { 752 if ($file != "." && $file != ".." && !is_link($ossrcfile."/".$file)) 753 { 754 if (is_dir($ossrcfile."/".$file)) 755 { 756 if (empty($excludesubdir) || ($excludesubdir == 2 && strlen($file) == 2)) { 757 $newfile = $file; 758 // Replace destination filename with a new one 759 if (is_array($arrayreplacement)) 760 { 761 foreach ($arrayreplacement as $key => $val) 762 { 763 $newfile = str_replace($key, $val, $newfile); 764 } 765 } 766 //var_dump("xxx dolCopyDir $srcfile/$file, $destfile/$file, $newmask, $overwriteifexists"); 767 $tmpresult = dolCopyDir($srcfile."/".$file, $destfile."/".$newfile, $newmask, $overwriteifexists, $arrayreplacement, $excludesubdir); 768 } 769 } else { 770 $newfile = $file; 771 // Replace destination filename with a new one 772 if (is_array($arrayreplacement)) 773 { 774 foreach ($arrayreplacement as $key => $val) 775 { 776 $newfile = str_replace($key, $val, $newfile); 777 } 778 } 779 $tmpresult = dol_copy($srcfile."/".$file, $destfile."/".$newfile, $newmask, $overwriteifexists); 780 } 781 // Set result 782 if ($result > 0 && $tmpresult >= 0) 783 { 784 // Do nothing, so we don't set result to 0 if tmpresult is 0 and result was success in a previous pass 785 } else { 786 $result = $tmpresult; 787 } 788 if ($result < 0) break; 789 } 790 } 791 closedir($dir_handle); 792 } else { 793 // Source directory does not exists 794 $result = -2; 795 } 796 797 return $result; 798} 799 800 801/** 802 * Move a file into another name. 803 * Note: 804 * - This function differs from dol_move_uploaded_file, because it can be called in any context. 805 * - Database indexes for files are updated. 806 * - Test on antivirus is done only if param testvirus is provided and an antivirus was set. 807 * 808 * @param string $srcfile Source file (can't be a directory. use native php @rename() to move a directory) 809 * @param string $destfile Destination file (can't be a directory. use native php @rename() to move a directory) 810 * @param integer $newmask Mask in octal string for new file (0 by default means $conf->global->MAIN_UMASK) 811 * @param int $overwriteifexists Overwrite file if exists (1 by default) 812 * @param int $testvirus Do an antivirus test. Move is canceled if a virus is found. 813 * @param int $indexdatabase Index new file into database. 814 * @return boolean True if OK, false if KO 815 * @see dol_move_uploaded_file() 816 */ 817function dol_move($srcfile, $destfile, $newmask = 0, $overwriteifexists = 1, $testvirus = 0, $indexdatabase = 1) 818{ 819 global $user, $db, $conf; 820 $result = false; 821 822 dol_syslog("files.lib.php::dol_move srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwritifexists=".$overwriteifexists); 823 $srcexists = dol_is_file($srcfile); 824 $destexists = dol_is_file($destfile); 825 826 if (!$srcexists) 827 { 828 dol_syslog("files.lib.php::dol_move srcfile does not exists. we ignore the move request."); 829 return false; 830 } 831 832 if ($overwriteifexists || !$destexists) 833 { 834 $newpathofsrcfile = dol_osencode($srcfile); 835 $newpathofdestfile = dol_osencode($destfile); 836 837 // Check virus 838 $testvirusarray = array(); 839 if ($testvirus) 840 { 841 $testvirusarray = dolCheckVirus($newpathofsrcfile); 842 if (count($testvirusarray)) 843 { 844 dol_syslog("files.lib.php::dol_move canceled because a virus was found into source file. we ignore the move request.", LOG_WARNING); 845 return false; 846 } 847 } 848 849 $result = @rename($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @ 850 if (!$result) 851 { 852 if ($destexists) 853 { 854 dol_syslog("files.lib.php::dol_move Failed. We try to delete target first and move after.", LOG_WARNING); 855 // We force delete and try again. Rename function sometimes fails to replace dest file with some windows NTFS partitions. 856 dol_delete_file($destfile); 857 $result = @rename($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @ 858 } else dol_syslog("files.lib.php::dol_move Failed.", LOG_WARNING); 859 } 860 861 // Move ok 862 if ($result && $indexdatabase) 863 { 864 // Rename entry into ecm database 865 $rel_filetorenamebefore = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $srcfile); 866 $rel_filetorenameafter = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $destfile); 867 if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetorenameafter)) // If not a tmp file 868 { 869 $rel_filetorenamebefore = preg_replace('/^[\\/]/', '', $rel_filetorenamebefore); 870 $rel_filetorenameafter = preg_replace('/^[\\/]/', '', $rel_filetorenameafter); 871 //var_dump($rel_filetorenamebefore.' - '.$rel_filetorenameafter);exit; 872 873 dol_syslog("Try to rename also entries in database for full relative path before = ".$rel_filetorenamebefore." after = ".$rel_filetorenameafter, LOG_DEBUG); 874 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; 875 876 $ecmfiletarget = new EcmFiles($db); 877 $resultecmtarget = $ecmfiletarget->fetch(0, '', $rel_filetorenameafter); 878 if ($resultecmtarget > 0) // An entry for target name already exists for target, we delete it, a new one will be created. 879 { 880 $ecmfiletarget->delete($user); 881 } 882 883 $ecmfile = new EcmFiles($db); 884 $resultecm = $ecmfile->fetch(0, '', $rel_filetorenamebefore); 885 if ($resultecm > 0) // If an entry was found for src file, we use it to move entry 886 { 887 $filename = basename($rel_filetorenameafter); 888 $rel_dir = dirname($rel_filetorenameafter); 889 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); 890 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); 891 892 $ecmfile->filepath = $rel_dir; 893 $ecmfile->filename = $filename; 894 895 $resultecm = $ecmfile->update($user); 896 } elseif ($resultecm == 0) // If no entry were found for src files, create/update target file 897 { 898 $filename = basename($rel_filetorenameafter); 899 $rel_dir = dirname($rel_filetorenameafter); 900 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); 901 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); 902 903 $ecmfile->filepath = $rel_dir; 904 $ecmfile->filename = $filename; 905 $ecmfile->label = md5_file(dol_osencode($destfile)); // $destfile is a full path to file 906 $ecmfile->fullpath_orig = $srcfile; 907 $ecmfile->gen_or_uploaded = 'unknown'; 908 $ecmfile->description = ''; // indexed content 909 $ecmfile->keyword = ''; // keyword content 910 $resultecm = $ecmfile->create($user); 911 if ($resultecm < 0) 912 { 913 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings'); 914 } 915 } elseif ($resultecm < 0) 916 { 917 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings'); 918 } 919 920 if ($resultecm > 0) $result = true; 921 else $result = false; 922 } 923 } 924 925 if (empty($newmask)) $newmask = empty($conf->global->MAIN_UMASK) ? '0755' : $conf->global->MAIN_UMASK; 926 $newmaskdec = octdec($newmask); 927 // Currently method is restricted to files (dol_delete_files previously used is for files, and mask usage if for files too) 928 // to allow mask usage for dir, we shoul introduce a new param "isdir" to 1 to complete newmask like this 929 // if ($isdir) $newmaskdec |= octdec('0111'); // Set x bit required for directories 930 @chmod($newpathofdestfile, $newmaskdec); 931 } 932 933 return $result; 934} 935 936/** 937 * Unescape a file submitted by upload. 938 * PHP escape char " (%22) or char ' (%27) into $FILES. 939 * 940 * @param string $filename Filename 941 * @return string Filename sanitized 942 */ 943function dol_unescapefile($filename) 944{ 945 // Remove path information and dots around the filename, to prevent uploading 946 // into different directories or replacing hidden system files. 947 // Also remove control characters and spaces (\x00..\x20) around the filename: 948 return trim(basename($filename), ".\x00..\x20"); 949} 950 951 952/** 953 * Check virus into a file 954 * 955 * @param string $src_file Source file to check 956 * @return array Array of errors or empty array if not virus found 957 */ 958function dolCheckVirus($src_file) 959{ 960 global $conf; 961 962 if (!empty($conf->global->MAIN_ANTIVIRUS_COMMAND)) 963 { 964 if (!class_exists('AntiVir')) { 965 require_once DOL_DOCUMENT_ROOT.'/core/class/antivir.class.php'; 966 } 967 $antivir = new AntiVir($db); 968 $result = $antivir->dol_avscan_file($src_file); 969 if ($result < 0) // If virus or error, we stop here 970 { 971 $reterrors = $antivir->errors; 972 return $reterrors; 973 } 974 } 975 return array(); 976} 977 978 979/** 980 * Make control on an uploaded file from an GUI page and move it to final destination. 981 * If there is errors (virus found, antivir in error, bad filename), file is not moved. 982 * Note: 983 * - This function can be used only into a HTML page context. Use dol_move if you are outside. 984 * - Test on antivirus is always done (if antivirus set). 985 * - Database of files is NOT updated (this is done by dol_add_file_process() that calls this function). 986 * - Extension .noexe may be added if file is executable and MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED is not set. 987 * 988 * @param string $src_file Source full path filename ($_FILES['field']['tmp_name']) 989 * @param string $dest_file Target full path filename ($_FILES['field']['name']) 990 * @param int $allowoverwrite 1=Overwrite target file if it already exists 991 * @param int $disablevirusscan 1=Disable virus scan 992 * @param integer $uploaderrorcode Value of PHP upload error code ($_FILES['field']['error']) 993 * @param int $nohook Disable all hooks 994 * @param string $varfiles _FILES var name 995 * @param string $upload_dir For information. Already included into $dest_file. 996 * @return int|string 1 if OK, 2 if OK and .noexe appended, <0 or string if KO 997 * @see dol_move() 998 */ 999function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan = 0, $uploaderrorcode = 0, $nohook = 0, $varfiles = 'addedfile', $upload_dir = '') 1000{ 1001 global $conf, $db, $user, $langs; 1002 global $object, $hookmanager; 1003 1004 $reshook = 0; 1005 $file_name = $dest_file; 1006 $successcode = 1; 1007 1008 if (empty($nohook)) 1009 { 1010 $reshook = $hookmanager->initHooks(array('fileslib')); 1011 1012 $parameters = array('dest_file' => $dest_file, 'src_file' => $src_file, 'file_name' => $file_name, 'varfiles' => $varfiles, 'allowoverwrite' => $allowoverwrite); 1013 $reshook = $hookmanager->executeHooks('moveUploadedFile', $parameters, $object); 1014 } 1015 1016 if (empty($reshook)) 1017 { 1018 // If an upload error has been reported 1019 if ($uploaderrorcode) 1020 { 1021 switch ($uploaderrorcode) 1022 { 1023 case UPLOAD_ERR_INI_SIZE: // 1 1024 return 'ErrorFileSizeTooLarge'; 1025 case UPLOAD_ERR_FORM_SIZE: // 2 1026 return 'ErrorFileSizeTooLarge'; 1027 case UPLOAD_ERR_PARTIAL: // 3 1028 return 'ErrorPartialFile'; 1029 case UPLOAD_ERR_NO_TMP_DIR: // 1030 return 'ErrorNoTmpDir'; 1031 case UPLOAD_ERR_CANT_WRITE: 1032 return 'ErrorFailedToWriteInDir'; 1033 case UPLOAD_ERR_EXTENSION: 1034 return 'ErrorUploadBlockedByAddon'; 1035 default: 1036 break; 1037 } 1038 } 1039 1040 // If we need to make a virus scan 1041 if (empty($disablevirusscan) && file_exists($src_file)) 1042 { 1043 $checkvirusarray = dolCheckVirus($src_file); 1044 if (count($checkvirusarray)) 1045 { 1046 dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$dest_file.'") KO with antivirus: errors='.join(',', $checkvirusarray), LOG_WARNING); 1047 return 'ErrorFileIsInfectedWithAVirus: '.join(',', $checkvirusarray); 1048 } 1049 } 1050 1051 // Security: 1052 // Disallow file with some extensions. We rename them. 1053 // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code. 1054 if (isAFileWithExecutableContent($dest_file) && empty($conf->global->MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED)) 1055 { 1056 // $upload_dir ends with a slash, so be must be sure the medias dir to compare to ends with slash too. 1057 $publicmediasdirwithslash = $conf->medias->multidir_output[$conf->entity]; 1058 if (!preg_match('/\/$/', $publicmediasdirwithslash)) $publicmediasdirwithslash .= '/'; 1059 1060 if (strpos($upload_dir, $publicmediasdirwithslash) !== 0) { // We never add .noexe on files into media directory 1061 $file_name .= '.noexe'; 1062 $successcode = 2; 1063 } 1064 } 1065 1066 // Security: 1067 // We refuse cache files/dirs, upload using .. and pipes into filenames. 1068 if (preg_match('/^\./', basename($src_file)) || preg_match('/\.\./', $src_file) || preg_match('/[<>|]/', $src_file)) 1069 { 1070 dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING); 1071 return -1; 1072 } 1073 1074 // Security: 1075 // We refuse cache files/dirs, upload using .. and pipes into filenames. 1076 if (preg_match('/^\./', basename($dest_file)) || preg_match('/\.\./', $dest_file) || preg_match('/[<>|]/', $dest_file)) 1077 { 1078 dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING); 1079 return -2; 1080 } 1081 } 1082 1083 if ($reshook < 0) { // At least one blocking error returned by one hook 1084 $errmsg = join(',', $hookmanager->errors); 1085 if (empty($errmsg)) $errmsg = 'ErrorReturnedBySomeHooks'; // Should not occurs. Added if hook is bugged and does not set ->errors when there is error. 1086 return $errmsg; 1087 } elseif (empty($reshook)) { 1088 // The file functions must be in OS filesystem encoding. 1089 $src_file_osencoded = dol_osencode($src_file); 1090 $file_name_osencoded = dol_osencode($file_name); 1091 1092 // Check if destination dir is writable 1093 if (!is_writable(dirname($file_name_osencoded))) 1094 { 1095 dol_syslog("Files.lib::dol_move_uploaded_file Dir ".dirname($file_name_osencoded)." is not writable. Return 'ErrorDirNotWritable'", LOG_WARNING); 1096 return 'ErrorDirNotWritable'; 1097 } 1098 1099 // Check if destination file already exists 1100 if (!$allowoverwrite) 1101 { 1102 if (file_exists($file_name_osencoded)) 1103 { 1104 dol_syslog("Files.lib::dol_move_uploaded_file File ".$file_name." already exists. Return 'ErrorFileAlreadyExists'", LOG_WARNING); 1105 return 'ErrorFileAlreadyExists'; 1106 } 1107 } else { // We are allowed to erase 1108 if (is_dir($file_name_osencoded)) { // If there is a directory with name of file to create 1109 dol_syslog("Files.lib::dol_move_uploaded_file A directory with name ".$file_name." already exists. Return 'ErrorDirWithFileNameAlreadyExists'", LOG_WARNING); 1110 return 'ErrorDirWithFileNameAlreadyExists'; 1111 } 1112 } 1113 1114 // Move file 1115 $return = move_uploaded_file($src_file_osencoded, $file_name_osencoded); 1116 if ($return) 1117 { 1118 if (!empty($conf->global->MAIN_UMASK)) @chmod($file_name_osencoded, octdec($conf->global->MAIN_UMASK)); 1119 dol_syslog("Files.lib::dol_move_uploaded_file Success to move ".$src_file." to ".$file_name." - Umask=".$conf->global->MAIN_UMASK, LOG_DEBUG); 1120 return $successcode; // Success 1121 } else { 1122 dol_syslog("Files.lib::dol_move_uploaded_file Failed to move ".$src_file." to ".$file_name, LOG_ERR); 1123 return -3; // Unknown error 1124 } 1125 } 1126 1127 return $successcode; // Success 1128} 1129 1130/** 1131 * Remove a file or several files with a mask. 1132 * This delete file physically but also database indexes. 1133 * 1134 * @param string $file File to delete or mask of files to delete 1135 * @param int $disableglob Disable usage of glob like * so function is an exact delete function that will return error if no file found 1136 * @param int $nophperrors Disable all PHP output errors 1137 * @param int $nohook Disable all hooks 1138 * @param object $object Current object in use 1139 * @param boolean $allowdotdot Allow to delete file path with .. inside. Never use this, it is reserved for migration purpose. 1140 * @param int $indexdatabase Try to remove also index entries. 1141 * @return boolean True if no error (file is deleted or if glob is used and there's nothing to delete), False if error 1142 * @see dol_delete_dir() 1143 */ 1144function dol_delete_file($file, $disableglob = 0, $nophperrors = 0, $nohook = 0, $object = null, $allowdotdot = false, $indexdatabase = 1) 1145{ 1146 global $db, $conf, $user, $langs; 1147 global $hookmanager; 1148 1149 // Load translation files required by the page 1150 $langs->loadLangs(array('other', 'errors')); 1151 1152 dol_syslog("dol_delete_file file=".$file." disableglob=".$disableglob." nophperrors=".$nophperrors." nohook=".$nohook); 1153 1154 // Security: 1155 // We refuse transversal using .. and pipes into filenames. 1156 if ((!$allowdotdot && preg_match('/\.\./', $file)) || preg_match('/[<>|]/', $file)) 1157 { 1158 dol_syslog("Refused to delete file ".$file, LOG_WARNING); 1159 return false; 1160 } 1161 1162 $reshook = 0; 1163 if (empty($nohook)) 1164 { 1165 $hookmanager->initHooks(array('fileslib')); 1166 1167 $parameters = array( 1168 'GET' => $_GET, 1169 'file' => $file, 1170 'disableglob'=> $disableglob, 1171 'nophperrors' => $nophperrors 1172 ); 1173 $reshook = $hookmanager->executeHooks('deleteFile', $parameters, $object); 1174 } 1175 1176 if (empty($nohook) && $reshook != 0) // reshook = 0 to do standard actions, 1 = ok and replace, -1 = ko 1177 { 1178 dol_syslog("reshook=".$reshook); 1179 if ($reshook < 0) return false; 1180 return true; 1181 } else { 1182 $file_osencoded = dol_osencode($file); // New filename encoded in OS filesystem encoding charset 1183 if (empty($disableglob) && !empty($file_osencoded)) 1184 { 1185 $ok = true; 1186 $globencoded = str_replace('[', '\[', $file_osencoded); 1187 $globencoded = str_replace(']', '\]', $globencoded); 1188 $listofdir = glob($globencoded); 1189 if (!empty($listofdir) && is_array($listofdir)) 1190 { 1191 foreach ($listofdir as $filename) 1192 { 1193 if ($nophperrors) $ok = @unlink($filename); 1194 else $ok = unlink($filename); 1195 1196 // If it fails and it is because of the missing write permission on parent dir 1197 if (!$ok && file_exists(dirname($filename)) && !(fileperms(dirname($filename)) & 0200)) { 1198 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); 1199 @chmod(dirname($filename), fileperms(dirname($filename)) | 0200); 1200 // Now we retry deletion 1201 if ($nophperrors) $ok = @unlink($filename); 1202 else $ok = unlink($filename); 1203 } 1204 1205 if ($ok) 1206 { 1207 dol_syslog("Removed file ".$filename, LOG_DEBUG); 1208 1209 // Delete entry into ecm database 1210 $rel_filetodelete = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filename); 1211 if (!preg_match('/(\/temp\/|\/thumbs\/|\.meta$)/', $rel_filetodelete)) // If not a tmp file 1212 { 1213 if (is_object($db) && $indexdatabase) // $db may not be defined when lib is in a context with define('NOREQUIREDB',1) 1214 { 1215 $rel_filetodelete = preg_replace('/^[\\/]/', '', $rel_filetodelete); 1216 $rel_filetodelete = preg_replace('/\.noexe$/', '', $rel_filetodelete); 1217 1218 dol_syslog("Try to remove also entries in database for full relative path = ".$rel_filetodelete, LOG_DEBUG); 1219 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; 1220 $ecmfile = new EcmFiles($db); 1221 $result = $ecmfile->fetch(0, '', $rel_filetodelete); 1222 if ($result >= 0 && $ecmfile->id > 0) 1223 { 1224 $result = $ecmfile->delete($user); 1225 } 1226 if ($result < 0) 1227 { 1228 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings'); 1229 } 1230 } 1231 } 1232 } else { 1233 dol_syslog("Failed to remove file ".$filename, LOG_WARNING); 1234 // TODO Failure to remove can be because file was already removed or because of permission 1235 // If error because it does not exists, we should return true, and we should return false if this is a permission problem 1236 } 1237 } 1238 } else { 1239 dol_syslog("No files to delete found", LOG_DEBUG); 1240 } 1241 } else { 1242 $ok = false; 1243 if ($nophperrors) $ok = @unlink($file_osencoded); 1244 else $ok = unlink($file_osencoded); 1245 if ($ok) dol_syslog("Removed file ".$file_osencoded, LOG_DEBUG); 1246 else dol_syslog("Failed to remove file ".$file_osencoded, LOG_WARNING); 1247 } 1248 1249 return $ok; 1250 } 1251} 1252 1253/** 1254 * Remove a directory (not recursive, so content must be empty). 1255 * If directory is not empty, return false 1256 * 1257 * @param string $dir Directory to delete 1258 * @param int $nophperrors Disable all PHP output errors 1259 * @return boolean True if success, false if error 1260 * @see dol_delete_file() dolCopyDir() 1261 */ 1262function dol_delete_dir($dir, $nophperrors = 0) 1263{ 1264 // Security: 1265 // We refuse transversal using .. and pipes into filenames. 1266 if (preg_match('/\.\./', $dir) || preg_match('/[<>|]/', $dir)) 1267 { 1268 dol_syslog("Refused to delete dir ".$dir, LOG_WARNING); 1269 return false; 1270 } 1271 1272 $dir_osencoded = dol_osencode($dir); 1273 return ($nophperrors ? @rmdir($dir_osencoded) : rmdir($dir_osencoded)); 1274} 1275 1276/** 1277 * Remove a directory $dir and its subdirectories (or only files and subdirectories) 1278 * 1279 * @param string $dir Dir to delete 1280 * @param int $count Counter to count nb of elements found to delete 1281 * @param int $nophperrors Disable all PHP output errors 1282 * @param int $onlysub Delete only files and subdir, not main directory 1283 * @param int $countdeleted Counter to count nb of elements found really deleted 1284 * @return int Number of files and directory we try to remove. NB really removed is returned into var by reference $countdeleted. 1285 */ 1286function dol_delete_dir_recursive($dir, $count = 0, $nophperrors = 0, $onlysub = 0, &$countdeleted = 0) 1287{ 1288 dol_syslog("functions.lib:dol_delete_dir_recursive ".$dir, LOG_DEBUG); 1289 if (dol_is_dir($dir)) 1290 { 1291 $dir_osencoded = dol_osencode($dir); 1292 if ($handle = opendir("$dir_osencoded")) 1293 { 1294 while (false !== ($item = readdir($handle))) 1295 { 1296 if (!utf8_check($item)) $item = utf8_encode($item); // should be useless 1297 1298 if ($item != "." && $item != "..") 1299 { 1300 if (is_dir(dol_osencode("$dir/$item")) && !is_link(dol_osencode("$dir/$item"))) 1301 { 1302 $count = dol_delete_dir_recursive("$dir/$item", $count, $nophperrors, 0, $countdeleted); 1303 } else { 1304 $result = dol_delete_file("$dir/$item", 1, $nophperrors); 1305 $count++; 1306 if ($result) $countdeleted++; 1307 //else print 'Error on '.$item."\n"; 1308 } 1309 } 1310 } 1311 closedir($handle); 1312 1313 if (empty($onlysub)) 1314 { 1315 $result = dol_delete_dir($dir, $nophperrors); 1316 $count++; 1317 if ($result) $countdeleted++; 1318 //else print 'Error on '.$dir."\n"; 1319 } 1320 } 1321 } 1322 1323 return $count; 1324} 1325 1326 1327/** 1328 * Delete all preview files linked to object instance. 1329 * Note that preview image of PDF files is generated when required, by dol_banner_tab() for example. 1330 * 1331 * @param object $object Object to clean 1332 * @return int 0 if error, 1 if OK 1333 * @see dol_convert_file() 1334 */ 1335function dol_delete_preview($object) 1336{ 1337 global $langs, $conf; 1338 1339 // Define parent dir of elements 1340 $element = $object->element; 1341 1342 if ($object->element == 'order_supplier') $dir = $conf->fournisseur->commande->dir_output; 1343 elseif ($object->element == 'invoice_supplier') $dir = $conf->fournisseur->facture->dir_output; 1344 elseif ($object->element == 'project') $dir = $conf->projet->dir_output; 1345 elseif ($object->element == 'shipping') $dir = $conf->expedition->dir_output.'/sending'; 1346 elseif ($object->element == 'delivery') $dir = $conf->expedition->dir_output.'/receipt'; 1347 elseif ($object->element == 'fichinter') $dir = $conf->ficheinter->dir_output; 1348 else $dir = empty($conf->$element->dir_output) ? '' : $conf->$element->dir_output; 1349 1350 if (empty($dir)) return 'ErrorObjectNoSupportedByFunction'; 1351 1352 $refsan = dol_sanitizeFileName($object->ref); 1353 $dir = $dir."/".$refsan; 1354 $filepreviewnew = $dir."/".$refsan.".pdf_preview.png"; 1355 $filepreviewnewbis = $dir."/".$refsan.".pdf_preview-0.png"; 1356 $filepreviewold = $dir."/".$refsan.".pdf.png"; 1357 1358 // For new preview files 1359 if (file_exists($filepreviewnew) && is_writable($filepreviewnew)) 1360 { 1361 if (!dol_delete_file($filepreviewnew, 1)) 1362 { 1363 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewnew); 1364 return 0; 1365 } 1366 } 1367 if (file_exists($filepreviewnewbis) && is_writable($filepreviewnewbis)) 1368 { 1369 if (!dol_delete_file($filepreviewnewbis, 1)) 1370 { 1371 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewnewbis); 1372 return 0; 1373 } 1374 } 1375 // For old preview files 1376 if (file_exists($filepreviewold) && is_writable($filepreviewold)) 1377 { 1378 if (!dol_delete_file($filepreviewold, 1)) 1379 { 1380 $object->error = $langs->trans("ErrorFailedToDeleteFile", $filepreviewold); 1381 return 0; 1382 } 1383 } else { 1384 $multiple = $filepreviewold."."; 1385 for ($i = 0; $i < 20; $i++) 1386 { 1387 $preview = $multiple.$i; 1388 1389 if (file_exists($preview) && is_writable($preview)) 1390 { 1391 if (!dol_delete_file($preview, 1)) 1392 { 1393 $object->error = $langs->trans("ErrorFailedToOpenFile", $preview); 1394 return 0; 1395 } 1396 } 1397 } 1398 } 1399 1400 return 1; 1401} 1402 1403/** 1404 * Create a meta file with document file into same directory. 1405 * This make "grep" search possible. 1406 * This feature to generate the meta file is enabled only if option MAIN_DOC_CREATE_METAFILE is set. 1407 * 1408 * @param CommonObject $object Object 1409 * @return int 0 if do nothing, >0 if we update meta file too, <0 if KO 1410 */ 1411function dol_meta_create($object) 1412{ 1413 global $conf; 1414 1415 // Create meta file 1416 if (empty($conf->global->MAIN_DOC_CREATE_METAFILE)) return 0; // By default, no metafile. 1417 1418 // Define parent dir of elements 1419 $element = $object->element; 1420 1421 if ($object->element == 'order_supplier') $dir = $conf->fournisseur->dir_output.'/commande'; 1422 elseif ($object->element == 'invoice_supplier') $dir = $conf->fournisseur->dir_output.'/facture'; 1423 elseif ($object->element == 'project') $dir = $conf->projet->dir_output; 1424 elseif ($object->element == 'shipping') $dir = $conf->expedition->dir_output.'/sending'; 1425 elseif ($object->element == 'delivery') $dir = $conf->expedition->dir_output.'/receipt'; 1426 elseif ($object->element == 'fichinter') $dir = $conf->ficheinter->dir_output; 1427 else $dir = empty($conf->$element->dir_output) ? '' : $conf->$element->dir_output; 1428 1429 if ($dir) 1430 { 1431 $object->fetch_thirdparty(); 1432 1433 $objectref = dol_sanitizeFileName($object->ref); 1434 $dir = $dir."/".$objectref; 1435 $file = $dir."/".$objectref.".meta"; 1436 1437 if (!is_dir($dir)) 1438 { 1439 dol_mkdir($dir); 1440 } 1441 1442 if (is_dir($dir)) 1443 { 1444 $nblines = count($object->lines); 1445 $client = $object->thirdparty->name." ".$object->thirdparty->address." ".$object->thirdparty->zip." ".$object->thirdparty->town; 1446 $meta = "REFERENCE=\"".$object->ref."\" 1447 DATE=\"" . dol_print_date($object->date, '')."\" 1448 NB_ITEMS=\"" . $nblines."\" 1449 CLIENT=\"" . $client."\" 1450 AMOUNT_EXCL_TAX=\"" . $object->total_ht."\" 1451 AMOUNT=\"" . $object->total_ttc."\"\n"; 1452 1453 for ($i = 0; $i < $nblines; $i++) 1454 { 1455 //Pour les articles 1456 $meta .= "ITEM_".$i."_QUANTITY=\"".$object->lines[$i]->qty."\" 1457 ITEM_" . $i."_AMOUNT_WO_TAX=\"".$object->lines[$i]->total_ht."\" 1458 ITEM_" . $i."_VAT=\"".$object->lines[$i]->tva_tx."\" 1459 ITEM_" . $i."_DESCRIPTION=\"".str_replace("\r\n", "", nl2br($object->lines[$i]->desc))."\" 1460 "; 1461 } 1462 } 1463 1464 $fp = fopen($file, "w"); 1465 fputs($fp, $meta); 1466 fclose($fp); 1467 if (!empty($conf->global->MAIN_UMASK)) 1468 @chmod($file, octdec($conf->global->MAIN_UMASK)); 1469 1470 return 1; 1471 } else { 1472 dol_syslog('FailedToDetectDirInDolMetaCreateFor'.$object->element, LOG_WARNING); 1473 } 1474 1475 return 0; 1476} 1477 1478 1479 1480/** 1481 * Scan a directory and init $_SESSION to manage uploaded files with list of all found files. 1482 * Note: Only email module seems to use this. Other feature initialize the $_SESSION doing $formmail->clear_attached_files(); $formmail->add_attached_files() 1483 * 1484 * @param string $pathtoscan Path to scan 1485 * @param string $trackid Track id (used to prefix name of session vars to avoid conflict) 1486 * @return void 1487 */ 1488function dol_init_file_process($pathtoscan = '', $trackid = '') 1489{ 1490 $listofpaths = array(); 1491 $listofnames = array(); 1492 $listofmimes = array(); 1493 1494 if ($pathtoscan) 1495 { 1496 $listoffiles = dol_dir_list($pathtoscan, 'files'); 1497 foreach ($listoffiles as $key => $val) 1498 { 1499 $listofpaths[] = $val['fullname']; 1500 $listofnames[] = $val['name']; 1501 $listofmimes[] = dol_mimetype($val['name']); 1502 } 1503 } 1504 $keytoavoidconflict = empty($trackid) ? '' : '-'.$trackid; 1505 $_SESSION["listofpaths".$keytoavoidconflict] = join(';', $listofpaths); 1506 $_SESSION["listofnames".$keytoavoidconflict] = join(';', $listofnames); 1507 $_SESSION["listofmimes".$keytoavoidconflict] = join(';', $listofmimes); 1508} 1509 1510 1511/** 1512 * Get and save an upload file (for example after submitting a new file a mail form). Database index of file is also updated if donotupdatesession is set. 1513 * All information used are in db, conf, langs, user and _FILES. 1514 * Note: This function can be used only into a HTML page context. 1515 * 1516 * @param string $upload_dir Directory where to store uploaded file (note: used to forge $destpath = $upload_dir + filename) 1517 * @param int $allowoverwrite 1=Allow overwrite existing file 1518 * @param int $donotupdatesession 1=Do no edit _SESSION variable but update database index. 0=Update _SESSION and not database index. -1=Do not update SESSION neither db. 1519 * @param string $varfiles _FILES var name 1520 * @param string $savingdocmask Mask to use to define output filename. For example 'XXXXX-__YYYYMMDD__-__file__' 1521 * @param string $link Link to add (to add a link instead of a file) 1522 * @param string $trackid Track id (used to prefix name of session vars to avoid conflict) 1523 * @param int $generatethumbs 1=Generate also thumbs for uploaded image files 1524 * @param Object $object Object used to set 'src_object_*' fields 1525 * @return int <=0 if KO, >0 if OK 1526 * @see dol_remove_file_process() 1527 */ 1528function dol_add_file_process($upload_dir, $allowoverwrite = 0, $donotupdatesession = 0, $varfiles = 'addedfile', $savingdocmask = '', $link = null, $trackid = '', $generatethumbs = 1, $object = null) 1529{ 1530 global $db, $user, $conf, $langs; 1531 1532 $res = 0; 1533 1534 if (!empty($_FILES[$varfiles])) // For view $_FILES[$varfiles]['error'] 1535 { 1536 dol_syslog('dol_add_file_process upload_dir='.$upload_dir.' allowoverwrite='.$allowoverwrite.' donotupdatesession='.$donotupdatesession.' savingdocmask='.$savingdocmask, LOG_DEBUG); 1537 1538 if (dol_mkdir($upload_dir) >= 0) 1539 { 1540 $TFile = $_FILES[$varfiles]; 1541 if (!is_array($TFile['name'])) 1542 { 1543 foreach ($TFile as $key => &$val) 1544 { 1545 $val = array($val); 1546 } 1547 } 1548 1549 $nbfile = count($TFile['name']); 1550 $nbok = 0; 1551 for ($i = 0; $i < $nbfile; $i++) 1552 { 1553 if (empty($TFile['name'][$i])) continue; // For example, when submitting a form with no file name 1554 1555 // Define $destfull (path to file including filename) and $destfile (only filename) 1556 $destfull = $upload_dir."/".$TFile['name'][$i]; 1557 $destfile = $TFile['name'][$i]; 1558 $destfilewithoutext = preg_replace('/\.[^\.]+$/', '', $destfile); 1559 1560 if ($savingdocmask && strpos($savingdocmask, $destfilewithoutext) !== 0) 1561 { 1562 $destfull = $upload_dir."/".preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask); 1563 $destfile = preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask); 1564 } 1565 1566 $filenameto = basename($destfile); 1567 if (preg_match('/^\./', $filenameto)) { 1568 $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now. 1569 setEventMessages($langs->trans("ErrorFilenameCantStartWithDot", $filenameto), null, 'errors'); 1570 break; 1571 } 1572 1573 // dol_sanitizeFileName the file name and lowercase extension 1574 $info = pathinfo($destfull); 1575 $destfull = $info['dirname'].'/'.dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : '')); 1576 $info = pathinfo($destfile); 1577 1578 $destfile = dol_sanitizeFileName($info['filename'].($info['extension'] != '' ? ('.'.strtolower($info['extension'])) : '')); 1579 1580 // We apply dol_string_nohtmltag also to clean file names (this remove duplicate spaces) because 1581 // this function is also applied when we rename and when we make try to download file (by the GETPOST(filename, 'alphanohtml') call). 1582 $destfile = dol_string_nohtmltag($destfile); 1583 $destfull = dol_string_nohtmltag($destfull); 1584 1585 // Move file from temp directory to final directory. A .noexe may also be appended on file name. 1586 $resupload = dol_move_uploaded_file($TFile['tmp_name'][$i], $destfull, $allowoverwrite, 0, $TFile['error'][$i], 0, $varfiles, $upload_dir); 1587 1588 if (is_numeric($resupload) && $resupload > 0) // $resupload can be 'ErrorFileAlreadyExists' 1589 { 1590 global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini; 1591 1592 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; 1593 1594 // Generate thumbs. 1595 if ($generatethumbs) 1596 { 1597 if (image_format_supported($destfull) == 1) 1598 { 1599 // Create thumbs 1600 // We can't use $object->addThumbs here because there is no $object known 1601 1602 // Used on logon for example 1603 $imgThumbSmall = vignette($destfull, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs"); 1604 // Create mini thumbs for image (Ratio is near 16/9) 1605 // Used on menu or for setup page for example 1606 $imgThumbMini = vignette($destfull, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs"); 1607 } 1608 } 1609 1610 // Update session 1611 if (empty($donotupdatesession)) 1612 { 1613 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; 1614 $formmail = new FormMail($db); 1615 $formmail->trackid = $trackid; 1616 $formmail->add_attached_files($destfull, $destfile, $TFile['type'][$i]); 1617 } 1618 1619 // Update index table of files (llx_ecm_files) 1620 if ($donotupdatesession == 1) 1621 { 1622 $result = addFileIntoDatabaseIndex($upload_dir, basename($destfile).($resupload == 2 ? '.noexe' : ''), $TFile['name'][$i], 'uploaded', 0, $object); 1623 if ($result < 0) 1624 { 1625 if ($allowoverwrite) { 1626 // Do not show error message. We can have an error due to DB_ERROR_RECORD_ALREADY_EXISTS 1627 } else { 1628 setEventMessages('WarningFailedToAddFileIntoDatabaseIndex', '', 'warnings'); 1629 } 1630 } 1631 } 1632 1633 $nbok++; 1634 } else { 1635 $langs->load("errors"); 1636 if ($resupload < 0) // Unknown error 1637 { 1638 setEventMessages($langs->trans("ErrorFileNotUploaded"), null, 'errors'); 1639 } elseif (preg_match('/ErrorFileIsInfectedWithAVirus/', $resupload)) // Files infected by a virus 1640 { 1641 setEventMessages($langs->trans("ErrorFileIsInfectedWithAVirus"), null, 'errors'); 1642 } else // Known error 1643 { 1644 setEventMessages($langs->trans($resupload), null, 'errors'); 1645 } 1646 } 1647 } 1648 if ($nbok > 0) 1649 { 1650 $res = 1; 1651 setEventMessages($langs->trans("FileTransferComplete"), null, 'mesgs'); 1652 } 1653 } 1654 } elseif ($link) { 1655 require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; 1656 $linkObject = new Link($db); 1657 $linkObject->entity = $conf->entity; 1658 $linkObject->url = $link; 1659 $linkObject->objecttype = GETPOST('objecttype', 'alpha'); 1660 $linkObject->objectid = GETPOST('objectid', 'int'); 1661 $linkObject->label = GETPOST('label', 'alpha'); 1662 $res = $linkObject->create($user); 1663 $langs->load('link'); 1664 if ($res > 0) { 1665 setEventMessages($langs->trans("LinkComplete"), null, 'mesgs'); 1666 } else { 1667 setEventMessages($langs->trans("ErrorFileNotLinked"), null, 'errors'); 1668 } 1669 } else { 1670 $langs->load("errors"); 1671 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("File")), null, 'errors'); 1672 } 1673 1674 return $res; 1675} 1676 1677 1678/** 1679 * Remove an uploaded file (for example after submitting a new file a mail form). 1680 * All information used are in db, conf, langs, user and _FILES. 1681 * 1682 * @param int $filenb File nb to delete 1683 * @param int $donotupdatesession -1 or 1 = Do not update _SESSION variable 1684 * @param int $donotdeletefile 1=Do not delete physically file 1685 * @param string $trackid Track id (used to prefix name of session vars to avoid conflict) 1686 * @return void 1687 * @see dol_add_file_process() 1688 */ 1689function dol_remove_file_process($filenb, $donotupdatesession = 0, $donotdeletefile = 1, $trackid = '') 1690{ 1691 global $db, $user, $conf, $langs, $_FILES; 1692 1693 $keytodelete = $filenb; 1694 $keytodelete--; 1695 1696 $listofpaths = array(); 1697 $listofnames = array(); 1698 $listofmimes = array(); 1699 $keytoavoidconflict = empty($trackid) ? '' : '-'.$trackid; 1700 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]); 1701 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]); 1702 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]); 1703 1704 if ($keytodelete >= 0) 1705 { 1706 $pathtodelete = $listofpaths[$keytodelete]; 1707 $filetodelete = $listofnames[$keytodelete]; 1708 if (empty($donotdeletefile)) $result = dol_delete_file($pathtodelete, 1); // The delete of ecm database is inside the function dol_delete_file 1709 else $result = 0; 1710 if ($result >= 0) 1711 { 1712 if (empty($donotdeletefile)) 1713 { 1714 $langs->load("other"); 1715 setEventMessages($langs->trans("FileWasRemoved", $filetodelete), null, 'mesgs'); 1716 } 1717 if (empty($donotupdatesession)) 1718 { 1719 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; 1720 $formmail = new FormMail($db); 1721 $formmail->trackid = $trackid; 1722 $formmail->remove_attached_files($keytodelete); 1723 } 1724 } 1725 } 1726} 1727 1728 1729/** 1730 * Add a file into database index. 1731 * Called by dol_add_file_process when uploading a file and on other cases. 1732 * See also commonGenerateDocument that also add/update database index when a file is generated. 1733 * 1734 * @param string $dir Directory name (full real path without ending /) 1735 * @param string $file File name (May end with '.noexe') 1736 * @param string $fullpathorig Full path of origin for file (can be '') 1737 * @param string $mode How file was created ('uploaded', 'generated', ...) 1738 * @param int $setsharekey Set also the share key 1739 * @param Object $object Object used to set 'src_object_*' fields 1740 * @return int <0 if KO, 0 if nothing done, >0 if OK 1741 */ 1742function addFileIntoDatabaseIndex($dir, $file, $fullpathorig = '', $mode = 'uploaded', $setsharekey = 0, $object = null) 1743{ 1744 global $db, $user; 1745 1746 $result = 0; 1747 1748 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir); 1749 1750 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) // If not a tmp dir 1751 { 1752 $filename = basename(preg_replace('/\.noexe$/', '', $file)); 1753 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); 1754 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); 1755 1756 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; 1757 $ecmfile = new EcmFiles($db); 1758 $ecmfile->filepath = $rel_dir; 1759 $ecmfile->filename = $filename; 1760 $ecmfile->label = md5_file(dol_osencode($dir.'/'.$file)); // MD5 of file content 1761 $ecmfile->fullpath_orig = $fullpathorig; 1762 $ecmfile->gen_or_uploaded = $mode; 1763 $ecmfile->description = ''; // indexed content 1764 $ecmfile->keyword = ''; // keyword content 1765 1766 if (is_object($object) && $object->id > 0) { 1767 $ecmfile->src_object_id = $object->id; 1768 $ecmfile->src_object_type = $object->table_element; 1769 } 1770 1771 if ($setsharekey) 1772 { 1773 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; 1774 $ecmfile->share = getRandomPassword(true); 1775 } 1776 1777 $result = $ecmfile->create($user); 1778 if ($result < 0) 1779 { 1780 dol_syslog($ecmfile->error); 1781 } 1782 } 1783 1784 return $result; 1785} 1786 1787/** 1788 * Delete files into database index using search criterias. 1789 * 1790 * @param string $dir Directory name (full real path without ending /) 1791 * @param string $file File name 1792 * @param string $mode How file was created ('uploaded', 'generated', ...) 1793 * @return int <0 if KO, 0 if nothing done, >0 if OK 1794 */ 1795function deleteFilesIntoDatabaseIndex($dir, $file, $mode = 'uploaded') 1796{ 1797 global $conf, $db, $user; 1798 1799 $error = 0; 1800 1801 if (empty($dir)) 1802 { 1803 dol_syslog("deleteFilesIntoDatabaseIndex: dir parameter can't be empty", LOG_ERR); 1804 return -1; 1805 } 1806 1807 $db->begin(); 1808 1809 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir); 1810 1811 $filename = basename($file); 1812 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); 1813 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); 1814 1815 if (!$error) 1816 { 1817 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'ecm_files'; 1818 $sql .= ' WHERE entity = '.$conf->entity; 1819 $sql .= " AND filepath = '".$db->escape($rel_dir)."'"; 1820 if ($file) $sql .= " AND filename = '".$db->escape($file)."'"; 1821 if ($mode) $sql .= " AND gen_or_uploaded = '".$db->escape($mode)."'"; 1822 1823 $resql = $db->query($sql); 1824 if (!$resql) 1825 { 1826 $error++; 1827 dol_syslog(__METHOD__.' '.$db->lasterror(), LOG_ERR); 1828 } 1829 } 1830 1831 // Commit or rollback 1832 if ($error) { 1833 $db->rollback(); 1834 return -1 * $error; 1835 } else { 1836 $db->commit(); 1837 return 1; 1838 } 1839} 1840 1841 1842/** 1843 * Convert an image file into another format. 1844 * This need Imagick php extension. 1845 * 1846 * @param string $fileinput Input file name 1847 * @param string $ext Format of target file (It is also extension added to file if fileoutput is not provided). 1848 * @param string $fileoutput Output filename 1849 * @param string $page Page number if we convert a PDF into png 1850 * @return int <0 if KO, 0=Nothing done, >0 if OK 1851 */ 1852function dol_convert_file($fileinput, $ext = 'png', $fileoutput = '', $page = '') 1853{ 1854 global $langs; 1855 if (class_exists('Imagick')) 1856 { 1857 $image = new Imagick(); 1858 try { 1859 $filetoconvert = $fileinput.(($page != '') ? '['.$page.']' : ''); 1860 //var_dump($filetoconvert); 1861 $ret = $image->readImage($filetoconvert); 1862 } catch (Exception $e) { 1863 $ext = pathinfo($fileinput, PATHINFO_EXTENSION); 1864 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." convertion in /etc/ImageMagick*/policy.xml): ".$e->getMessage(), LOG_WARNING); 1865 return 0; 1866 } 1867 if ($ret) 1868 { 1869 $ret = $image->setImageFormat($ext); 1870 if ($ret) 1871 { 1872 if (empty($fileoutput)) $fileoutput = $fileinput.".".$ext; 1873 1874 $count = $image->getNumberImages(); 1875 1876 if (!dol_is_file($fileoutput) || is_writeable($fileoutput)) 1877 { 1878 try { 1879 $ret = $image->writeImages($fileoutput, true); 1880 } catch (Exception $e) 1881 { 1882 dol_syslog($e->getMessage(), LOG_WARNING); 1883 } 1884 } else { 1885 dol_syslog("Warning: Failed to write cache preview file '.$fileoutput.'. Check permission on file/dir", LOG_ERR); 1886 } 1887 if ($ret) return $count; 1888 else return -3; 1889 } else { 1890 return -2; 1891 } 1892 } else { 1893 return -1; 1894 } 1895 } else { 1896 return 0; 1897 } 1898} 1899 1900 1901/** 1902 * Compress a file. 1903 * An error string may be returned into parameters. 1904 * 1905 * @param string $inputfile Source file name 1906 * @param string $outputfile Target file name 1907 * @param string $mode 'gz' or 'bz' or 'zip' 1908 * @param string $errorstring Error string 1909 * @return int <0 if KO, >0 if OK 1910 */ 1911function dol_compress_file($inputfile, $outputfile, $mode = "gz", &$errorstring = null) 1912{ 1913 global $conf; 1914 1915 $foundhandler = 0; 1916 1917 try { 1918 dol_syslog("dol_compress_file mode=".$mode." inputfile=".$inputfile." outputfile=".$outputfile); 1919 1920 $data = implode("", file(dol_osencode($inputfile))); 1921 if ($mode == 'gz') { $foundhandler = 1; $compressdata = gzencode($data, 9); } elseif ($mode == 'bz') { $foundhandler = 1; $compressdata = bzcompress($data, 9); } elseif ($mode == 'zip') 1922 { 1923 if (class_exists('ZipArchive') && !empty($conf->global->MAIN_USE_ZIPARCHIVE_FOR_ZIP_COMPRESS)) 1924 { 1925 $foundhandler = 1; 1926 1927 $rootPath = realpath($inputfile); 1928 1929 dol_syslog("Class ZipArchive is set so we zip using ZipArchive to zip into ".$outputfile.' rootPath='.$rootPath); 1930 $zip = new ZipArchive; 1931 1932 if ($zip->open($outputfile, ZipArchive::CREATE) !== true) { 1933 $errorstring = "dol_compress_file failure - Failed to open file ".$outputfile."\n"; 1934 dol_syslog($errorstring, LOG_ERR); 1935 1936 global $errormsg; 1937 $errormsg = $errorstring; 1938 1939 return -6; 1940 } 1941 1942 // Create recursive directory iterator 1943 /** @var SplFileInfo[] $files */ 1944 $files = new RecursiveIteratorIterator( 1945 new RecursiveDirectoryIterator($rootPath), 1946 RecursiveIteratorIterator::LEAVES_ONLY 1947 ); 1948 1949 foreach ($files as $name => $file) 1950 { 1951 // Skip directories (they would be added automatically) 1952 if (!$file->isDir()) 1953 { 1954 // Get real and relative path for current file 1955 $filePath = $file->getRealPath(); 1956 $relativePath = substr($filePath, strlen($rootPath) + 1); 1957 1958 // Add current file to archive 1959 $zip->addFile($filePath, $relativePath); 1960 } 1961 } 1962 1963 // Zip archive will be created only after closing object 1964 $zip->close(); 1965 1966 dol_syslog("dol_compress_file success - ".count($zip->numFiles)." files"); 1967 return 1; 1968 } 1969 1970 if (defined('ODTPHP_PATHTOPCLZIP')) 1971 { 1972 $foundhandler = 1; 1973 1974 include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php'; 1975 $archive = new PclZip($outputfile); 1976 $result = $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile)); 1977 1978 if ($result === 0) 1979 { 1980 global $errormsg; 1981 $errormsg = $archive->errorInfo(true); 1982 1983 if ($archive->errorCode() == PCLZIP_ERR_WRITE_OPEN_FAIL) 1984 { 1985 $errorstring = "PCLZIP_ERR_WRITE_OPEN_FAIL"; 1986 dol_syslog("dol_compress_file error - archive->errorCode() = PCLZIP_ERR_WRITE_OPEN_FAIL", LOG_ERR); 1987 return -4; 1988 } 1989 1990 $errorstring = "dol_compress_file error archive->errorCode = ".$archive->errorCode()." errormsg=".$errormsg; 1991 dol_syslog("dol_compress_file failure - ".$errormsg, LOG_ERR); 1992 return -3; 1993 } else { 1994 dol_syslog("dol_compress_file success - ".count($result)." files"); 1995 return 1; 1996 } 1997 } 1998 } 1999 2000 if ($foundhandler) 2001 { 2002 $fp = fopen($outputfile, "w"); 2003 fwrite($fp, $compressdata); 2004 fclose($fp); 2005 return 1; 2006 } else { 2007 $errorstring = "Try to zip with format ".$mode." with no handler for this format"; 2008 dol_syslog($errorstring, LOG_ERR); 2009 2010 global $errormsg; 2011 $errormsg = $errorstring; 2012 return -2; 2013 } 2014 } catch (Exception $e) 2015 { 2016 global $langs, $errormsg; 2017 $langs->load("errors"); 2018 $errormsg = $langs->trans("ErrorFailedToWriteInDir"); 2019 2020 $errorstring = "Failed to open file ".$outputfile; 2021 dol_syslog($errorstring, LOG_ERR); 2022 return -1; 2023 } 2024} 2025 2026/** 2027 * Uncompress a file 2028 * 2029 * @param string $inputfile File to uncompress 2030 * @param string $outputdir Target dir name 2031 * @return array array('error'=>'Error code') or array() if no error 2032 */ 2033function dol_uncompress($inputfile, $outputdir) 2034{ 2035 global $conf, $langs; 2036 2037 if (defined('ODTPHP_PATHTOPCLZIP') && empty($conf->global->MAIN_USE_ZIPARCHIVE_FOR_ZIP_UNCOMPRESS)) 2038 { 2039 dol_syslog("Constant ODTPHP_PATHTOPCLZIP for pclzip library is set to ".ODTPHP_PATHTOPCLZIP.", so we use Pclzip to unzip into ".$outputdir); 2040 include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php'; 2041 $archive = new PclZip($inputfile); 2042 2043 // Extract into outputdir, but only files that match the regex '/^((?!\.\.).)*$/' that means "does not include .." 2044 $result = $archive->extract(PCLZIP_OPT_PATH, $outputdir, PCLZIP_OPT_BY_PREG, '/^((?!\.\.).)*$/'); 2045 2046 if (!is_array($result) && $result <= 0) return array('error'=>$archive->errorInfo(true)); 2047 else { 2048 $ok = 1; $errmsg = ''; 2049 // Loop on each file to check result for unzipping file 2050 foreach ($result as $key => $val) 2051 { 2052 if ($val['status'] == 'path_creation_fail') 2053 { 2054 $langs->load("errors"); 2055 $ok = 0; 2056 $errmsg = $langs->trans("ErrorFailToCreateDir", $val['filename']); 2057 break; 2058 } 2059 } 2060 2061 if ($ok) return array(); 2062 else return array('error'=>$errmsg); 2063 } 2064 } 2065 2066 if (class_exists('ZipArchive')) // Must install php-zip to have it 2067 { 2068 dol_syslog("Class ZipArchive is set so we unzip using ZipArchive to unzip into ".$outputdir); 2069 $zip = new ZipArchive; 2070 $res = $zip->open($inputfile); 2071 if ($res === true) 2072 { 2073 //$zip->extractTo($outputdir.'/'); 2074 // We must extract one file at time so we can check that file name does not contains '..' to avoid transversal path of zip built for example using 2075 // python3 path_traversal_archiver.py <Created_file_name> test.zip -l 10 -p tmp/ 2076 // with -l is the range of dot to go back in path. 2077 // and path_traversal_archiver.py found at https://github.com/Alamot/code-snippets/blob/master/path_traversal/path_traversal_archiver.py 2078 for ($i = 0; $i < $zip->numFiles; $i++) { 2079 if (preg_match('/\.\./', $zip->getNameIndex($i))) { 2080 dol_syslog("Warning: Try to unzip a file with a transversal path ".$zip->getNameIndex($i), LOG_WARNING); 2081 continue; // Discard the file 2082 } 2083 $zip->extractTo($outputdir.'/', array($zip->getNameIndex($i))); 2084 } 2085 2086 $zip->close(); 2087 return array(); 2088 } else { 2089 return array('error'=>'ErrUnzipFails'); 2090 } 2091 } 2092 2093 return array('error'=>'ErrNoZipEngine'); 2094} 2095 2096 2097/** 2098 * Compress a directory and subdirectories into a package file. 2099 * 2100 * @param string $inputdir Source dir name 2101 * @param string $outputfile Target file name (output directory must exists and be writable) 2102 * @param string $mode 'zip' 2103 * @param string $excludefiles A regex pattern. For example: '/\.log$|\/temp\//' 2104 * @param string $rootdirinzip Add a root dir level in zip file 2105 * @return int <0 if KO, >0 if OK 2106 */ 2107function dol_compress_dir($inputdir, $outputfile, $mode = "zip", $excludefiles = '', $rootdirinzip = '') 2108{ 2109 $foundhandler = 0; 2110 2111 dol_syslog("Try to zip dir ".$inputdir." into ".$outputfile." mode=".$mode); 2112 2113 if (!dol_is_dir(dirname($outputfile)) || !is_writable(dirname($outputfile))) 2114 { 2115 global $langs, $errormsg; 2116 $langs->load("errors"); 2117 $errormsg = $langs->trans("ErrorFailedToWriteInDir", $outputfile); 2118 return -3; 2119 } 2120 2121 try { 2122 if ($mode == 'gz') { $foundhandler = 0; } elseif ($mode == 'bz') { $foundhandler = 0; } elseif ($mode == 'zip') 2123 { 2124 /*if (defined('ODTPHP_PATHTOPCLZIP')) 2125 { 2126 $foundhandler=0; // TODO implement this 2127 2128 include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php'; 2129 $archive = new PclZip($outputfile); 2130 $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile)); 2131 //$archive->add($inputfile); 2132 return 1; 2133 } 2134 else*/ 2135 //if (class_exists('ZipArchive') && ! empty($conf->global->MAIN_USE_ZIPARCHIVE_FOR_ZIP_COMPRESS)) 2136 if (class_exists('ZipArchive')) 2137 { 2138 $foundhandler = 1; 2139 2140 // Initialize archive object 2141 $zip = new ZipArchive(); 2142 $result = $zip->open($outputfile, ZipArchive::CREATE | ZipArchive::OVERWRITE); 2143 if (!$result) 2144 { 2145 global $langs, $errormsg; 2146 $langs->load("errors"); 2147 $errormsg = $langs->trans("ErrorFailedToWriteInFile", $outputfile); 2148 return -4; 2149 } 2150 2151 // Create recursive directory iterator 2152 /** @var SplFileInfo[] $files */ 2153 $files = new RecursiveIteratorIterator( 2154 new RecursiveDirectoryIterator($inputdir), 2155 RecursiveIteratorIterator::LEAVES_ONLY 2156 ); 2157 2158 foreach ($files as $name => $file) 2159 { 2160 // Skip directories (they would be added automatically) 2161 if (!$file->isDir()) 2162 { 2163 // Get real and relative path for current file 2164 $filePath = $file->getRealPath(); 2165 $relativePath = ($rootdirinzip ? $rootdirinzip.'/' : '').substr($filePath, strlen($inputdir) + 1); 2166 2167 if (empty($excludefiles) || !preg_match($excludefiles, $filePath)) 2168 { 2169 // Add current file to archive 2170 $zip->addFile($filePath, $relativePath); 2171 } 2172 } 2173 } 2174 2175 // Zip archive will be created only after closing object 2176 $zip->close(); 2177 2178 return 1; 2179 } 2180 } 2181 2182 if (!$foundhandler) 2183 { 2184 dol_syslog("Try to zip with format ".$mode." with no handler for this format", LOG_ERR); 2185 return -2; 2186 } else { 2187 return 0; 2188 } 2189 } catch (Exception $e) 2190 { 2191 global $langs, $errormsg; 2192 $langs->load("errors"); 2193 dol_syslog("Failed to open file ".$outputfile, LOG_ERR); 2194 dol_syslog($e->getMessage(), LOG_ERR); 2195 $errormsg = $langs->trans("ErrorFailedToWriteInDir", $outputfile); 2196 return -1; 2197 } 2198} 2199 2200 2201 2202/** 2203 * Return file(s) into a directory (by default most recent) 2204 * 2205 * @param string $dir Directory to scan 2206 * @param string $regexfilter Regex filter to restrict list. This regex value must be escaped for '/', since this char is used for preg_match function 2207 * @param array $excludefilter Array of Regex for exclude filter (example: array('(\.meta|_preview.*\.png)$','^\.')). This regex value must be escaped for '/', since this char is used for preg_match function 2208 * @param int $nohook Disable all hooks 2209 * @param int $mode 0=Return array minimum keys loaded (faster), 1=Force all keys like date and size to be loaded (slower), 2=Force load of date only, 3=Force load of size only 2210 * @return string Full path to most recent file 2211 */ 2212function dol_most_recent_file($dir, $regexfilter = '', $excludefilter = array('(\.meta|_preview.*\.png)$', '^\.'), $nohook = false, $mode = '') 2213{ 2214 $tmparray = dol_dir_list($dir, 'files', 0, $regexfilter, $excludefilter, 'date', SORT_DESC, $mode, $nohook); 2215 return $tmparray[0]; 2216} 2217 2218/** 2219 * Security check when accessing to a document (used by document.php, viewimage.php and webservices) 2220 * 2221 * @param string $modulepart Module of document ('module', 'module_user_temp', 'module_user' or 'module_temp') 2222 * @param string $original_file Relative path with filename, relative to modulepart. 2223 * @param string $entity Restrict onto entity (0=no restriction) 2224 * @param User $fuser User object (forced) 2225 * @param string $refname Ref of object to check permission for external users (autodetect if not provided) 2226 * @param string $mode Check permission for 'read' or 'write' 2227 * @return mixed Array with access information : 'accessallowed' & 'sqlprotectagainstexternals' & 'original_file' (as a full path name) 2228 * @see restrictedArea() 2229 */ 2230function dol_check_secure_access_document($modulepart, $original_file, $entity, $fuser = '', $refname = '', $mode = 'read') 2231{ 2232 global $conf, $db, $user; 2233 global $dolibarr_main_data_root, $dolibarr_main_document_root_alt; 2234 2235 if (!is_object($fuser)) $fuser = $user; 2236 2237 if (empty($modulepart)) return 'ErrorBadParameter'; 2238 if (empty($entity)) 2239 { 2240 if (empty($conf->multicompany->enabled)) $entity = 1; 2241 else $entity = 0; 2242 } 2243 // Fix modulepart 2244 if ($modulepart == 'users') $modulepart = 'user'; 2245 2246 dol_syslog('dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity); 2247 2248 // We define $accessallowed and $sqlprotectagainstexternals 2249 $accessallowed = 0; 2250 $sqlprotectagainstexternals = ''; 2251 $ret = array(); 2252 2253 // Find the subdirectory name as the reference. For exemple original_file='10/myfile.pdf' -> refname='10' 2254 if (empty($refname)) $refname = basename(dirname($original_file)."/"); 2255 2256 // Define possible keys to use for permission check 2257 $lire = 'lire'; $read = 'read'; $download = 'download'; 2258 if ($mode == 'write') 2259 { 2260 $lire = 'creer'; $read = 'write'; $download = 'upload'; 2261 } 2262 2263 // Wrapping for miscellaneous medias files 2264 if ($modulepart == 'medias' && !empty($dolibarr_main_data_root)) 2265 { 2266 if (empty($entity) || empty($conf->medias->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided'); 2267 $accessallowed = 1; 2268 $original_file = $conf->medias->multidir_output[$entity].'/'.$original_file; 2269 } // Wrapping for *.log files, like when used with url http://.../document.php?modulepart=logs&file=dolibarr.log 2270 elseif ($modulepart == 'logs' && !empty($dolibarr_main_data_root)) 2271 { 2272 $accessallowed = ($user->admin && basename($original_file) == $original_file && preg_match('/^dolibarr.*\.log$/', basename($original_file))); 2273 $original_file = $dolibarr_main_data_root.'/'.$original_file; 2274 } // Wrapping for *.log files, like when used with url http://.../document.php?modulepart=logs&file=dolibarr.log 2275 elseif ($modulepart == 'doctemplates' && !empty($dolibarr_main_data_root)) 2276 { 2277 $accessallowed = $user->admin; 2278 $original_file = $dolibarr_main_data_root.'/doctemplates/'.$original_file; 2279 } // Wrapping for *.zip files, like when used with url http://.../document.php?modulepart=packages&file=module_myfile.zip 2280 elseif ($modulepart == 'doctemplateswebsite' && !empty($dolibarr_main_data_root)) 2281 { 2282 $accessallowed = ($fuser->rights->website->write && preg_match('/\.jpg$/i', basename($original_file))); 2283 $original_file = $dolibarr_main_data_root.'/doctemplates/websites/'.$original_file; 2284 } // Wrapping for *.zip files, like when used with url http://.../document.php?modulepart=packages&file=module_myfile.zip 2285 elseif ($modulepart == 'packages' && !empty($dolibarr_main_data_root)) 2286 { 2287 // Dir for custom dirs 2288 $tmp = explode(',', $dolibarr_main_document_root_alt); 2289 $dirins = $tmp[0]; 2290 2291 $accessallowed = ($user->admin && preg_match('/^module_.*\.zip$/', basename($original_file))); 2292 $original_file = $dirins.'/'.$original_file; 2293 } // Wrapping for some images 2294 elseif ($modulepart == 'mycompany' && !empty($conf->mycompany->dir_output)) 2295 { 2296 $accessallowed = 1; 2297 $original_file = $conf->mycompany->dir_output.'/'.$original_file; 2298 } // Wrapping for users photos 2299 elseif ($modulepart == 'userphoto' && !empty($conf->user->dir_output)) 2300 { 2301 $accessallowed = 1; 2302 $original_file = $conf->user->dir_output.'/'.$original_file; 2303 } // Wrapping for members photos 2304 elseif ($modulepart == 'memberphoto' && !empty($conf->adherent->dir_output)) 2305 { 2306 $accessallowed = 1; 2307 $original_file = $conf->adherent->dir_output.'/'.$original_file; 2308 } // Wrapping pour les apercu factures 2309 elseif ($modulepart == 'apercufacture' && !empty($conf->facture->multidir_output[$entity])) 2310 { 2311 if ($fuser->rights->facture->{$lire}) $accessallowed = 1; 2312 $original_file = $conf->facture->multidir_output[$entity].'/'.$original_file; 2313 } // Wrapping pour les apercu propal 2314 elseif ($modulepart == 'apercupropal' && !empty($conf->propal->multidir_output[$entity])) 2315 { 2316 if ($fuser->rights->propale->{$lire}) $accessallowed = 1; 2317 $original_file = $conf->propal->multidir_output[$entity].'/'.$original_file; 2318 } // Wrapping pour les apercu commande 2319 elseif ($modulepart == 'apercucommande' && !empty($conf->commande->multidir_output[$entity])) 2320 { 2321 if ($fuser->rights->commande->{$lire}) $accessallowed = 1; 2322 $original_file = $conf->commande->multidir_output[$entity].'/'.$original_file; 2323 } // Wrapping pour les apercu intervention 2324 elseif (($modulepart == 'apercufichinter' || $modulepart == 'apercuficheinter') && !empty($conf->ficheinter->dir_output)) 2325 { 2326 if ($fuser->rights->ficheinter->{$lire}) $accessallowed = 1; 2327 $original_file = $conf->ficheinter->dir_output.'/'.$original_file; 2328 } // Wrapping pour les apercu conat 2329 elseif (($modulepart == 'apercucontract') && !empty($conf->contrat->multidir_output[$entity])) 2330 { 2331 if ($fuser->rights->contrat->{$lire}) $accessallowed = 1; 2332 $original_file = $conf->contrat->multidir_output[$entity].'/'.$original_file; 2333 } // Wrapping pour les apercu supplier proposal 2334 elseif (($modulepart == 'apercusupplier_proposal' || $modulepart == 'apercusupplier_proposal') && !empty($conf->supplier_proposal->dir_output)) 2335 { 2336 if ($fuser->rights->supplier_proposal->{$lire}) $accessallowed = 1; 2337 $original_file = $conf->supplier_proposal->dir_output.'/'.$original_file; 2338 } // Wrapping pour les apercu supplier order 2339 elseif (($modulepart == 'apercusupplier_order' || $modulepart == 'apercusupplier_order') && !empty($conf->fournisseur->commande->dir_output)) 2340 { 2341 if ($fuser->rights->fournisseur->commande->{$lire}) $accessallowed = 1; 2342 $original_file = $conf->fournisseur->commande->dir_output.'/'.$original_file; 2343 } // Wrapping pour les apercu supplier invoice 2344 elseif (($modulepart == 'apercusupplier_invoice' || $modulepart == 'apercusupplier_invoice') && !empty($conf->fournisseur->facture->dir_output)) 2345 { 2346 if ($fuser->rights->fournisseur->facture->{$lire}) $accessallowed = 1; 2347 $original_file = $conf->fournisseur->facture->dir_output.'/'.$original_file; 2348 } // Wrapping pour les apercu supplier invoice 2349 elseif (($modulepart == 'apercuexpensereport') && !empty($conf->expensereport->dir_output)) 2350 { 2351 if ($fuser->rights->expensereport->{$lire}) $accessallowed = 1; 2352 $original_file = $conf->expensereport->dir_output.'/'.$original_file; 2353 } // Wrapping pour les images des stats propales 2354 elseif ($modulepart == 'propalstats' && !empty($conf->propal->multidir_temp[$entity])) 2355 { 2356 if ($fuser->rights->propale->{$lire}) $accessallowed = 1; 2357 $original_file = $conf->propal->multidir_temp[$entity].'/'.$original_file; 2358 } // Wrapping pour les images des stats commandes 2359 elseif ($modulepart == 'orderstats' && !empty($conf->commande->dir_temp)) 2360 { 2361 if ($fuser->rights->commande->{$lire}) $accessallowed = 1; 2362 $original_file = $conf->commande->dir_temp.'/'.$original_file; 2363 } elseif ($modulepart == 'orderstatssupplier' && !empty($conf->fournisseur->dir_output)) 2364 { 2365 if ($fuser->rights->fournisseur->commande->{$lire}) $accessallowed = 1; 2366 $original_file = $conf->fournisseur->commande->dir_temp.'/'.$original_file; 2367 } // Wrapping pour les images des stats factures 2368 elseif ($modulepart == 'billstats' && !empty($conf->facture->dir_temp)) 2369 { 2370 if ($fuser->rights->facture->{$lire}) $accessallowed = 1; 2371 $original_file = $conf->facture->dir_temp.'/'.$original_file; 2372 } elseif ($modulepart == 'billstatssupplier' && !empty($conf->fournisseur->dir_output)) 2373 { 2374 if ($fuser->rights->fournisseur->facture->{$lire}) $accessallowed = 1; 2375 $original_file = $conf->fournisseur->facture->dir_temp.'/'.$original_file; 2376 } // Wrapping pour les images des stats expeditions 2377 elseif ($modulepart == 'expeditionstats' && !empty($conf->expedition->dir_temp)) 2378 { 2379 if ($fuser->rights->expedition->{$lire}) $accessallowed = 1; 2380 $original_file = $conf->expedition->dir_temp.'/'.$original_file; 2381 } // Wrapping pour les images des stats expeditions 2382 elseif ($modulepart == 'tripsexpensesstats' && !empty($conf->deplacement->dir_temp)) 2383 { 2384 if ($fuser->rights->deplacement->{$lire}) $accessallowed = 1; 2385 $original_file = $conf->deplacement->dir_temp.'/'.$original_file; 2386 } // Wrapping pour les images des stats expeditions 2387 elseif ($modulepart == 'memberstats' && !empty($conf->adherent->dir_temp)) 2388 { 2389 if ($fuser->rights->adherent->{$lire}) $accessallowed = 1; 2390 $original_file = $conf->adherent->dir_temp.'/'.$original_file; 2391 } // Wrapping pour les images des stats produits 2392 elseif (preg_match('/^productstats_/i', $modulepart) && !empty($conf->product->dir_temp)) 2393 { 2394 if ($fuser->rights->produit->{$lire} || $fuser->rights->service->{$lire}) $accessallowed = 1; 2395 $original_file = (!empty($conf->product->multidir_temp[$entity]) ? $conf->product->multidir_temp[$entity] : $conf->service->multidir_temp[$entity]).'/'.$original_file; 2396 } // Wrapping for taxes 2397 elseif (in_array($modulepart, array('tax', 'tax-vat')) && !empty($conf->tax->dir_output)) 2398 { 2399 if ($fuser->rights->tax->charges->{$lire}) $accessallowed = 1; 2400 $modulepartsuffix = str_replace('tax-', '', $modulepart); 2401 $original_file = $conf->tax->dir_output.'/'.($modulepartsuffix != 'tax' ? $modulepartsuffix.'/' : '').$original_file; 2402 } // Wrapping for events 2403 elseif ($modulepart == 'actions' && !empty($conf->agenda->dir_output)) 2404 { 2405 if ($fuser->rights->agenda->myactions->{$read}) $accessallowed = 1; 2406 $original_file = $conf->agenda->dir_output.'/'.$original_file; 2407 } // Wrapping for categories 2408 elseif ($modulepart == 'category' && !empty($conf->categorie->multidir_output[$entity])) 2409 { 2410 if (empty($entity) || empty($conf->categorie->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided'); 2411 if ($fuser->rights->categorie->{$lire}) $accessallowed = 1; 2412 $original_file = $conf->categorie->multidir_output[$entity].'/'.$original_file; 2413 } // Wrapping pour les prelevements 2414 elseif ($modulepart == 'prelevement' && !empty($conf->prelevement->dir_output)) 2415 { 2416 if ($fuser->rights->prelevement->bons->{$lire} || preg_match('/^specimen/i', $original_file)) $accessallowed = 1; 2417 $original_file = $conf->prelevement->dir_output.'/'.$original_file; 2418 } // Wrapping pour les graph energie 2419 elseif ($modulepart == 'graph_stock' && !empty($conf->stock->dir_temp)) 2420 { 2421 $accessallowed = 1; 2422 $original_file = $conf->stock->dir_temp.'/'.$original_file; 2423 } // Wrapping pour les graph fournisseurs 2424 elseif ($modulepart == 'graph_fourn' && !empty($conf->fournisseur->dir_temp)) 2425 { 2426 $accessallowed = 1; 2427 $original_file = $conf->fournisseur->dir_temp.'/'.$original_file; 2428 } // Wrapping pour les graph des produits 2429 elseif ($modulepart == 'graph_product' && !empty($conf->product->dir_temp)) 2430 { 2431 $accessallowed = 1; 2432 $original_file = $conf->product->multidir_temp[$entity].'/'.$original_file; 2433 } // Wrapping pour les code barre 2434 elseif ($modulepart == 'barcode') 2435 { 2436 $accessallowed = 1; 2437 // If viewimage is called for barcode, we try to output an image on the fly, with no build of file on disk. 2438 //$original_file=$conf->barcode->dir_temp.'/'.$original_file; 2439 $original_file = ''; 2440 } // Wrapping pour les icones de background des mailings 2441 elseif ($modulepart == 'iconmailing' && !empty($conf->mailing->dir_temp)) 2442 { 2443 $accessallowed = 1; 2444 $original_file = $conf->mailing->dir_temp.'/'.$original_file; 2445 } // Wrapping pour le scanner 2446 elseif ($modulepart == 'scanner_user_temp' && !empty($conf->scanner->dir_temp)) 2447 { 2448 $accessallowed = 1; 2449 $original_file = $conf->scanner->dir_temp.'/'.$fuser->id.'/'.$original_file; 2450 } // Wrapping pour les images fckeditor 2451 elseif ($modulepart == 'fckeditor' && !empty($conf->fckeditor->dir_output)) 2452 { 2453 $accessallowed = 1; 2454 $original_file = $conf->fckeditor->dir_output.'/'.$original_file; 2455 } // Wrapping for users 2456 elseif ($modulepart == 'user' && !empty($conf->user->dir_output)) 2457 { 2458 $canreaduser = (!empty($fuser->admin) || $fuser->rights->user->user->{$lire}); 2459 if ($fuser->id == (int) $refname) { $canreaduser = 1; } // A user can always read its own card 2460 if ($canreaduser || preg_match('/^specimen/i', $original_file)) 2461 { 2462 $accessallowed = 1; 2463 } 2464 $original_file = $conf->user->dir_output.'/'.$original_file; 2465 } // Wrapping for third parties 2466 elseif (($modulepart == 'company' || $modulepart == 'societe' || $modulepart == 'thirdparty') && !empty($conf->societe->multidir_output[$entity])) 2467 { 2468 if (empty($entity) || empty($conf->societe->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided'); 2469 if ($fuser->rights->societe->{$lire} || preg_match('/^specimen/i', $original_file)) 2470 { 2471 $accessallowed = 1; 2472 } 2473 $original_file = $conf->societe->multidir_output[$entity].'/'.$original_file; 2474 $sqlprotectagainstexternals = "SELECT rowid as fk_soc FROM ".MAIN_DB_PREFIX."societe WHERE rowid='".$db->escape($refname)."' AND entity IN (".getEntity('societe').")"; 2475 } // Wrapping for contact 2476 elseif ($modulepart == 'contact' && !empty($conf->societe->multidir_output[$entity])) 2477 { 2478 if (empty($entity) || empty($conf->societe->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided'); 2479 if ($fuser->rights->societe->{$lire}) 2480 { 2481 $accessallowed = 1; 2482 } 2483 $original_file = $conf->societe->multidir_output[$entity].'/contact/'.$original_file; 2484 } // Wrapping for invoices 2485 elseif (($modulepart == 'facture' || $modulepart == 'invoice') && !empty($conf->facture->multidir_output[$entity])) 2486 { 2487 if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i', $original_file)) 2488 { 2489 $accessallowed = 1; 2490 } 2491 $original_file = $conf->facture->multidir_output[$entity].'/'.$original_file; 2492 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('invoice').")"; 2493 } // Wrapping for mass actions 2494 elseif ($modulepart == 'massfilesarea_proposals' && !empty($conf->propal->multidir_output[$entity])) 2495 { 2496 if ($fuser->rights->propal->{$lire} || preg_match('/^specimen/i', $original_file)) 2497 { 2498 $accessallowed = 1; 2499 } 2500 $original_file = $conf->propal->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file; 2501 } elseif ($modulepart == 'massfilesarea_orders') 2502 { 2503 if ($fuser->rights->commande->{$lire} || preg_match('/^specimen/i', $original_file)) 2504 { 2505 $accessallowed = 1; 2506 } 2507 $original_file = $conf->commande->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file; 2508 } elseif ($modulepart == 'massfilesarea_sendings') 2509 { 2510 if ($fuser->rights->expedition->{$lire} || preg_match('/^specimen/i', $original_file)) 2511 { 2512 $accessallowed = 1; 2513 } 2514 $original_file = $conf->expedition->dir_output.'/sending/temp/massgeneration/'.$user->id.'/'.$original_file; 2515 } elseif ($modulepart == 'massfilesarea_invoices') 2516 { 2517 if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i', $original_file)) 2518 { 2519 $accessallowed = 1; 2520 } 2521 $original_file = $conf->facture->multidir_output[$entity].'/temp/massgeneration/'.$user->id.'/'.$original_file; 2522 } elseif ($modulepart == 'massfilesarea_expensereport') 2523 { 2524 if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i', $original_file)) 2525 { 2526 $accessallowed = 1; 2527 } 2528 $original_file = $conf->expensereport->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; 2529 } elseif ($modulepart == 'massfilesarea_interventions') 2530 { 2531 if ($fuser->rights->ficheinter->{$lire} || preg_match('/^specimen/i', $original_file)) 2532 { 2533 $accessallowed = 1; 2534 } 2535 $original_file = $conf->ficheinter->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; 2536 } elseif ($modulepart == 'massfilesarea_supplier_proposal' && !empty($conf->supplier_proposal->dir_output)) 2537 { 2538 if ($fuser->rights->supplier_proposal->{$lire} || preg_match('/^specimen/i', $original_file)) 2539 { 2540 $accessallowed = 1; 2541 } 2542 $original_file = $conf->supplier_proposal->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; 2543 } elseif ($modulepart == 'massfilesarea_supplier_order') 2544 { 2545 if ($fuser->rights->fournisseur->commande->{$lire} || preg_match('/^specimen/i', $original_file)) 2546 { 2547 $accessallowed = 1; 2548 } 2549 $original_file = $conf->fournisseur->commande->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; 2550 } elseif ($modulepart == 'massfilesarea_supplier_invoice') 2551 { 2552 if ($fuser->rights->fournisseur->facture->{$lire} || preg_match('/^specimen/i', $original_file)) 2553 { 2554 $accessallowed = 1; 2555 } 2556 $original_file = $conf->fournisseur->facture->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; 2557 } elseif ($modulepart == 'massfilesarea_contract' && !empty($conf->contrat->dir_output)) 2558 { 2559 if ($fuser->rights->contrat->{$lire} || preg_match('/^specimen/i', $original_file)) 2560 { 2561 $accessallowed = 1; 2562 } 2563 $original_file = $conf->contrat->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; 2564 } // Wrapping for interventions 2565 elseif (($modulepart == 'fichinter' || $modulepart == 'ficheinter') && !empty($conf->ficheinter->dir_output)) 2566 { 2567 if ($fuser->rights->ficheinter->{$lire} || preg_match('/^specimen/i', $original_file)) 2568 { 2569 $accessallowed = 1; 2570 } 2571 $original_file = $conf->ficheinter->dir_output.'/'.$original_file; 2572 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; 2573 } // Wrapping pour les deplacements et notes de frais 2574 elseif ($modulepart == 'deplacement' && !empty($conf->deplacement->dir_output)) 2575 { 2576 if ($fuser->rights->deplacement->{$lire} || preg_match('/^specimen/i', $original_file)) 2577 { 2578 $accessallowed = 1; 2579 } 2580 $original_file = $conf->deplacement->dir_output.'/'.$original_file; 2581 //$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; 2582 } // Wrapping pour les propales 2583 elseif (($modulepart == 'propal' || $modulepart == 'propale') && !empty($conf->propal->multidir_output[$entity])) 2584 { 2585 if ($fuser->rights->propale->{$lire} || preg_match('/^specimen/i', $original_file)) 2586 { 2587 $accessallowed = 1; 2588 } 2589 $original_file = $conf->propal->multidir_output[$entity].'/'.$original_file; 2590 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."propal WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('propal').")"; 2591 } // Wrapping pour les commandes 2592 elseif (($modulepart == 'commande' || $modulepart == 'order') && !empty($conf->commande->multidir_output[$entity])) 2593 { 2594 if ($fuser->rights->commande->{$lire} || preg_match('/^specimen/i', $original_file)) 2595 { 2596 $accessallowed = 1; 2597 } 2598 $original_file = $conf->commande->multidir_output[$entity].'/'.$original_file; 2599 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('order').")"; 2600 } // Wrapping pour les projets 2601 elseif ($modulepart == 'project' && !empty($conf->projet->dir_output)) 2602 { 2603 if ($fuser->rights->projet->{$lire} || preg_match('/^specimen/i', $original_file)) 2604 { 2605 $accessallowed = 1; 2606 } 2607 $original_file = $conf->projet->dir_output.'/'.$original_file; 2608 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")"; 2609 } elseif ($modulepart == 'project_task' && !empty($conf->projet->dir_output)) 2610 { 2611 if ($fuser->rights->projet->{$lire} || preg_match('/^specimen/i', $original_file)) 2612 { 2613 $accessallowed = 1; 2614 } 2615 $original_file = $conf->projet->dir_output.'/'.$original_file; 2616 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")"; 2617 } // Wrapping pour les commandes fournisseurs 2618 elseif (($modulepart == 'commande_fournisseur' || $modulepart == 'order_supplier') && !empty($conf->fournisseur->commande->dir_output)) 2619 { 2620 if ($fuser->rights->fournisseur->commande->{$lire} || preg_match('/^specimen/i', $original_file)) 2621 { 2622 $accessallowed = 1; 2623 } 2624 $original_file = $conf->fournisseur->commande->dir_output.'/'.$original_file; 2625 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; 2626 } // Wrapping pour les factures fournisseurs 2627 elseif (($modulepart == 'facture_fournisseur' || $modulepart == 'invoice_supplier') && !empty($conf->fournisseur->facture->dir_output)) 2628 { 2629 if ($fuser->rights->fournisseur->facture->{$lire} || preg_match('/^specimen/i', $original_file)) 2630 { 2631 $accessallowed = 1; 2632 } 2633 $original_file = $conf->fournisseur->facture->dir_output.'/'.$original_file; 2634 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture_fourn WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; 2635 } // Wrapping pour les rapport de paiements 2636 elseif ($modulepart == 'supplier_payment') 2637 { 2638 if ($fuser->rights->fournisseur->facture->{$lire} || preg_match('/^specimen/i', $original_file)) 2639 { 2640 $accessallowed = 1; 2641 } 2642 $original_file = $conf->fournisseur->payment->dir_output.'/'.$original_file; 2643 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."paiementfournisseur WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; 2644 } // Wrapping pour les rapport de paiements 2645 elseif ($modulepart == 'facture_paiement' && !empty($conf->facture->dir_output)) 2646 { 2647 if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i', $original_file)) 2648 { 2649 $accessallowed = 1; 2650 } 2651 if ($fuser->societe_id > 0) $original_file = $conf->facture->dir_output.'/payments/private/'.$fuser->id.'/'.$original_file; 2652 else $original_file = $conf->facture->dir_output.'/payments/'.$original_file; 2653 } // Wrapping for accounting exports 2654 elseif ($modulepart == 'export_compta' && !empty($conf->accounting->dir_output)) 2655 { 2656 if ($fuser->rights->accounting->bind->write || preg_match('/^specimen/i', $original_file)) 2657 { 2658 $accessallowed = 1; 2659 } 2660 $original_file = $conf->accounting->dir_output.'/'.$original_file; 2661 } // Wrapping pour les expedition 2662 elseif (($modulepart == 'expedition' || $modulepart == 'shipment') && !empty($conf->expedition->dir_output)) 2663 { 2664 if ($fuser->rights->expedition->{$lire} || preg_match('/^specimen/i', $original_file)) 2665 { 2666 $accessallowed = 1; 2667 } 2668 $original_file = $conf->expedition->dir_output."/sending/".$original_file; 2669 } // Delivery Note Wrapping 2670 elseif (($modulepart == 'livraison' || $modulepart == 'delivery') && !empty($conf->expedition->dir_output)) 2671 { 2672 if ($fuser->rights->expedition->delivery->{$lire} || preg_match('/^specimen/i', $original_file)) 2673 { 2674 $accessallowed = 1; 2675 } 2676 $original_file = $conf->expedition->dir_output."/receipt/".$original_file; 2677 } // Wrapping pour les actions 2678 elseif ($modulepart == 'actions' && !empty($conf->agenda->dir_output)) 2679 { 2680 if ($fuser->rights->agenda->myactions->{$read} || preg_match('/^specimen/i', $original_file)) 2681 { 2682 $accessallowed = 1; 2683 } 2684 $original_file = $conf->agenda->dir_output.'/'.$original_file; 2685 } // Wrapping pour les actions 2686 elseif ($modulepart == 'actionsreport' && !empty($conf->agenda->dir_temp)) 2687 { 2688 if ($fuser->rights->agenda->allactions->{$read} || preg_match('/^specimen/i', $original_file)) 2689 { 2690 $accessallowed = 1; 2691 } 2692 $original_file = $conf->agenda->dir_temp."/".$original_file; 2693 } // Wrapping pour les produits et services 2694 elseif ($modulepart == 'product' || $modulepart == 'produit' || $modulepart == 'service' || $modulepart == 'produit|service') 2695 { 2696 if (empty($entity) || (empty($conf->product->multidir_output[$entity]) && empty($conf->service->multidir_output[$entity]))) return array('accessallowed'=>0, 'error'=>'Value entity must be provided'); 2697 if (($fuser->rights->produit->{$lire} || $fuser->rights->service->{$lire}) || preg_match('/^specimen/i', $original_file)) 2698 { 2699 $accessallowed = 1; 2700 } 2701 if (!empty($conf->product->enabled)) $original_file = $conf->product->multidir_output[$entity].'/'.$original_file; 2702 elseif (!empty($conf->service->enabled)) $original_file = $conf->service->multidir_output[$entity].'/'.$original_file; 2703 } // Wrapping pour les lots produits 2704 elseif ($modulepart == 'product_batch' || $modulepart == 'produitlot') 2705 { 2706 if (empty($entity) || (empty($conf->productbatch->multidir_output[$entity]))) return array('accessallowed'=>0, 'error'=>'Value entity must be provided'); 2707 if (($fuser->rights->produit->{$lire} ) || preg_match('/^specimen/i', $original_file)) 2708 { 2709 $accessallowed = 1; 2710 } 2711 if (!empty($conf->productbatch->enabled)) $original_file = $conf->productbatch->multidir_output[$entity].'/'.$original_file; 2712 } // Wrapping for stock movements 2713 elseif ($modulepart == 'movement' || $modulepart == 'mouvement') 2714 { 2715 if (empty($entity) || empty($conf->stock->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided'); 2716 if (($fuser->rights->stock->{$lire} || $fuser->rights->stock->movement->{$lire} || $fuser->rights->stock->mouvement->{$lire}) || preg_match('/^specimen/i', $original_file)) 2717 { 2718 $accessallowed = 1; 2719 } 2720 if (!empty($conf->stock->enabled)) $original_file = $conf->stock->multidir_output[$entity].'/movement/'.$original_file; 2721 } // Wrapping pour les contrats 2722 elseif ($modulepart == 'contract' && !empty($conf->contrat->multidir_output[$entity])) 2723 { 2724 if ($fuser->rights->contrat->{$lire} || preg_match('/^specimen/i', $original_file)) 2725 { 2726 $accessallowed = 1; 2727 } 2728 $original_file = $conf->contrat->multidir_output[$entity].'/'.$original_file; 2729 $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."contrat WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('contract').")"; 2730 } // Wrapping pour les dons 2731 elseif ($modulepart == 'donation' && !empty($conf->don->dir_output)) 2732 { 2733 if ($fuser->rights->don->{$lire} || preg_match('/^specimen/i', $original_file)) 2734 { 2735 $accessallowed = 1; 2736 } 2737 $original_file = $conf->don->dir_output.'/'.$original_file; 2738 } // Wrapping pour les dons 2739 elseif ($modulepart == 'dolresource' && !empty($conf->resource->dir_output)) 2740 { 2741 if ($fuser->rights->resource->{$read} || preg_match('/^specimen/i', $original_file)) 2742 { 2743 $accessallowed = 1; 2744 } 2745 $original_file = $conf->resource->dir_output.'/'.$original_file; 2746 } // Wrapping pour les remises de cheques 2747 elseif ($modulepart == 'remisecheque' && !empty($conf->bank->dir_output)) 2748 { 2749 if ($fuser->rights->banque->{$lire} || preg_match('/^specimen/i', $original_file)) 2750 { 2751 $accessallowed = 1; 2752 } 2753 2754 $original_file = $conf->bank->dir_output.'/checkdeposits/'.$original_file; // original_file should contains relative path so include the get_exdir result 2755 } // Wrapping for bank 2756 elseif (($modulepart == 'banque' || $modulepart == 'bank') && !empty($conf->bank->dir_output)) 2757 { 2758 if ($fuser->rights->banque->{$lire}) 2759 { 2760 $accessallowed = 1; 2761 } 2762 $original_file = $conf->bank->dir_output.'/'.$original_file; 2763 } // Wrapping for export module 2764 elseif ($modulepart == 'export' && !empty($conf->export->dir_temp)) 2765 { 2766 // Aucun test necessaire car on force le rep de download sur 2767 // le rep export qui est propre a l'utilisateur 2768 $accessallowed = 1; 2769 $original_file = $conf->export->dir_temp.'/'.$fuser->id.'/'.$original_file; 2770 } // Wrapping for import module 2771 elseif ($modulepart == 'import' && !empty($conf->import->dir_temp)) 2772 { 2773 $accessallowed = 1; 2774 $original_file = $conf->import->dir_temp.'/'.$original_file; 2775 } // Wrapping pour l'editeur wysiwyg 2776 elseif ($modulepart == 'editor' && !empty($conf->fckeditor->dir_output)) 2777 { 2778 $accessallowed = 1; 2779 $original_file = $conf->fckeditor->dir_output.'/'.$original_file; 2780 } // Wrapping for backups 2781 elseif ($modulepart == 'systemtools' && !empty($conf->admin->dir_output)) 2782 { 2783 if ($fuser->admin) $accessallowed = 1; 2784 $original_file = $conf->admin->dir_output.'/'.$original_file; 2785 } // Wrapping for upload file test 2786 elseif ($modulepart == 'admin_temp' && !empty($conf->admin->dir_temp)) 2787 { 2788 if ($fuser->admin) $accessallowed = 1; 2789 $original_file = $conf->admin->dir_temp.'/'.$original_file; 2790 } // Wrapping pour BitTorrent 2791 elseif ($modulepart == 'bittorrent' && !empty($conf->bittorrent->dir_output)) 2792 { 2793 $accessallowed = 1; 2794 $dir = 'files'; 2795 if (dol_mimetype($original_file) == 'application/x-bittorrent') $dir = 'torrents'; 2796 $original_file = $conf->bittorrent->dir_output.'/'.$dir.'/'.$original_file; 2797 } // Wrapping pour Foundation module 2798 elseif ($modulepart == 'member' && !empty($conf->adherent->dir_output)) 2799 { 2800 if ($fuser->rights->adherent->{$lire} || preg_match('/^specimen/i', $original_file)) 2801 { 2802 $accessallowed = 1; 2803 } 2804 $original_file = $conf->adherent->dir_output.'/'.$original_file; 2805 } // Wrapping for Scanner 2806 elseif ($modulepart == 'scanner_user_temp' && !empty($conf->scanner->dir_temp)) 2807 { 2808 $accessallowed = 1; 2809 $original_file = $conf->scanner->dir_temp.'/'.$fuser->id.'/'.$original_file; 2810 } // GENERIC Wrapping 2811 // If modulepart=module_user_temp Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp/iduser 2812 // If modulepart=module_temp Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp 2813 // If modulepart=module_user Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/iduser 2814 // If modulepart=module Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart 2815 // If modulepart=module-abc Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart 2816 else { 2817 //var_dump($modulepart); 2818 //var_dump($original_file); 2819 if (preg_match('/^specimen/i', $original_file)) $accessallowed = 1; // If link to a file called specimen. Test must be done before changing $original_file int full path. 2820 if ($fuser->admin) $accessallowed = 1; // If user is admin 2821 $tmpmodulepart = explode('-', $modulepart); 2822 if (!empty($tmpmodulepart[1])) { 2823 $modulepart = $tmpmodulepart[0]; 2824 $original_file = $tmpmodulepart[1].'/'.$original_file; 2825 } 2826 2827 // Define $accessallowed 2828 $reg = array(); 2829 if (preg_match('/^([a-z]+)_user_temp$/i', $modulepart, $reg)) 2830 { 2831 if (empty($conf->{$reg[1]}->dir_temp)) // modulepart not supported 2832 { 2833 dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')'); 2834 exit; 2835 } 2836 if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) $accessallowed = 1; 2837 $original_file = $conf->{$reg[1]}->dir_temp.'/'.$fuser->id.'/'.$original_file; 2838 } elseif (preg_match('/^([a-z]+)_temp$/i', $modulepart, $reg)) 2839 { 2840 if (empty($conf->{$reg[1]}->dir_temp)) // modulepart not supported 2841 { 2842 dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')'); 2843 exit; 2844 } 2845 if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) $accessallowed = 1; 2846 $original_file = $conf->{$reg[1]}->dir_temp.'/'.$original_file; 2847 } elseif (preg_match('/^([a-z]+)_user$/i', $modulepart, $reg)) 2848 { 2849 if (empty($conf->{$reg[1]}->dir_output)) // modulepart not supported 2850 { 2851 dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')'); 2852 exit; 2853 } 2854 if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) $accessallowed = 1; 2855 $original_file = $conf->{$reg[1]}->dir_output.'/'.$fuser->id.'/'.$original_file; 2856 } elseif (preg_match('/^massfilesarea_([a-z]+)$/i', $modulepart, $reg)) 2857 { 2858 if (empty($conf->{$reg[1]}->dir_output)) // modulepart not supported 2859 { 2860 dol_print_error('', 'Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')'); 2861 exit; 2862 } 2863 if ($fuser->rights->{$reg[1]}->{$lire} || preg_match('/^specimen/i', $original_file)) 2864 { 2865 $accessallowed = 1; 2866 } 2867 $original_file = $conf->{$reg[1]}->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file; 2868 } else { 2869 if (empty($conf->$modulepart->dir_output)) // modulepart not supported 2870 { 2871 dol_print_error('', '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.'); 2872 exit; 2873 } 2874 2875 // Check fuser->rights->modulepart->myobject->read and fuser->rights->modulepart->read 2876 $partsofdirinoriginalfile = explode('/', $original_file); 2877 if (!empty($partsofdirinoriginalfile[1])) { // If original_file is xxx/filename (xxx is a part we will use) 2878 $partofdirinoriginalfile = $partsofdirinoriginalfile[0]; 2879 if ($partofdirinoriginalfile && !empty($fuser->rights->$modulepart->$partofdirinoriginalfile) && ($fuser->rights->$modulepart->$partofdirinoriginalfile->{$lire} || $fuser->rights->$modulepart->$partofdirinoriginalfile->{$read})) $accessallowed = 1; 2880 } 2881 if (!empty($fuser->rights->$modulepart->{$lire}) || !empty($fuser->rights->$modulepart->{$read})) $accessallowed = 1; 2882 2883 if (is_array($conf->$modulepart->multidir_output) && !empty($conf->$modulepart->multidir_output[$entity])) { 2884 $original_file = $conf->$modulepart->multidir_output[$entity].'/'.$original_file; 2885 } else { 2886 $original_file = $conf->$modulepart->dir_output.'/'.$original_file; 2887 } 2888 } 2889 2890 // For modules who wants to manage different levels of permissions for documents 2891 $subPermCategoryConstName = strtoupper($modulepart).'_SUBPERMCATEGORY_FOR_DOCUMENTS'; 2892 if (!empty($conf->global->$subPermCategoryConstName)) 2893 { 2894 $subPermCategory = $conf->global->$subPermCategoryConstName; 2895 if (!empty($subPermCategory) && (($fuser->rights->$modulepart->$subPermCategory->{$lire}) || ($fuser->rights->$modulepart->$subPermCategory->{$read}) || ($fuser->rights->$modulepart->$subPermCategory->{$download}))) 2896 { 2897 $accessallowed = 1; 2898 } 2899 } 2900 2901 // Define $sqlprotectagainstexternals for modules who want to protect access using a SQL query. 2902 $sqlProtectConstName = strtoupper($modulepart).'_SQLPROTECTAGAINSTEXTERNALS_FOR_DOCUMENTS'; 2903 if (!empty($conf->global->$sqlProtectConstName)) // If module want to define its own $sqlprotectagainstexternals 2904 { 2905 // Example: mymodule__SQLPROTECTAGAINSTEXTERNALS_FOR_DOCUMENTS = "SELECT fk_soc FROM ".MAIN_DB_PREFIX.$modulepart." WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; 2906 eval('$sqlprotectagainstexternals = "'.$conf->global->$sqlProtectConstName.'";'); 2907 } 2908 } 2909 2910 $ret = array( 2911 'accessallowed' => $accessallowed, 2912 'sqlprotectagainstexternals'=>$sqlprotectagainstexternals, 2913 'original_file'=>$original_file 2914 ); 2915 2916 return $ret; 2917} 2918 2919/** 2920 * Store object in file. 2921 * 2922 * @param string $directory Directory of cache 2923 * @param string $filename Name of filecache 2924 * @param mixed $object Object to store in cachefile 2925 * @return void 2926 */ 2927function dol_filecache($directory, $filename, $object) 2928{ 2929 if (!dol_is_dir($directory)) dol_mkdir($directory); 2930 $cachefile = $directory.$filename; 2931 file_put_contents($cachefile, serialize($object), LOCK_EX); 2932 @chmod($cachefile, 0644); 2933} 2934 2935/** 2936 * Test if Refresh needed. 2937 * 2938 * @param string $directory Directory of cache 2939 * @param string $filename Name of filecache 2940 * @param int $cachetime Cachetime delay 2941 * @return boolean 0 no refresh 1 if refresh needed 2942 */ 2943function dol_cache_refresh($directory, $filename, $cachetime) 2944{ 2945 $now = dol_now(); 2946 $cachefile = $directory.$filename; 2947 $refresh = !file_exists($cachefile) || ($now - $cachetime) > dol_filemtime($cachefile); 2948 return $refresh; 2949} 2950 2951/** 2952 * Read object from cachefile. 2953 * 2954 * @param string $directory Directory of cache 2955 * @param string $filename Name of filecache 2956 * @return mixed Unserialise from file 2957 */ 2958function dol_readcachefile($directory, $filename) 2959{ 2960 $cachefile = $directory.$filename; 2961 $object = unserialize(file_get_contents($cachefile)); 2962 return $object; 2963} 2964 2965 2966/** 2967 * Function to get list of updated or modified files. 2968 * $file_list is used as global variable 2969 * 2970 * @param array $file_list Array for response 2971 * @param SimpleXMLElement $dir SimpleXMLElement of files to test 2972 * @param string $path Path of files relative to $pathref. We start with ''. Used by recursive calls. 2973 * @param string $pathref Path ref (DOL_DOCUMENT_ROOT) 2974 * @param array $checksumconcat Array of checksum 2975 * @return array Array of filenames 2976 */ 2977function getFilesUpdated(&$file_list, SimpleXMLElement $dir, $path = '', $pathref = '', &$checksumconcat = array()) 2978{ 2979 global $conffile; 2980 2981 $exclude = 'install'; 2982 2983 foreach ($dir->md5file as $file) // $file is a simpleXMLElement 2984 { 2985 $filename = $path.$file['name']; 2986 $file_list['insignature'][] = $filename; 2987 $expectedsize = (empty($file['size']) ? '' : $file['size']); 2988 $expectedmd5 = (string) $file; 2989 2990 //if (preg_match('#'.$exclude.'#', $filename)) continue; 2991 2992 if (!file_exists($pathref.'/'.$filename)) 2993 { 2994 $file_list['missing'][] = array('filename'=>$filename, 'expectedmd5'=>$expectedmd5, 'expectedsize'=>$expectedsize); 2995 } else { 2996 $md5_local = md5_file($pathref.'/'.$filename); 2997 2998 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 2999 { 3000 $checksumconcat[] = $expectedmd5; 3001 } else { 3002 if ($md5_local != $expectedmd5) $file_list['updated'][] = array('filename'=>$filename, 'expectedmd5'=>$expectedmd5, 'expectedsize'=>$expectedsize, 'md5'=>(string) $md5_local); 3003 $checksumconcat[] = $md5_local; 3004 } 3005 } 3006 } 3007 3008 foreach ($dir->dir as $subdir) // $subdir['name'] is '' or '/accountancy/admin' for example 3009 { 3010 getFilesUpdated($file_list, $subdir, $path.$subdir['name'].'/', $pathref, $checksumconcat); 3011 } 3012 3013 return $file_list; 3014} 3015