diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 6affeeb29b9..173f6ddc76e 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -75,7 +75,7 @@ function dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefil $parameters=array( 'path' => $newpath, 'types'=> $types, - 'recursive' =>$recursive, + 'recursive' => $recursive, 'filter' => $filter, 'excludefilter' => $excludefilter, 'sortcriteria' => $sortcriteria, @@ -553,6 +553,136 @@ function dol_unescapefile($filename) return trim(basename($filename), ".\x00..\x20"); } +/** + * Move an uploaded file after some controls. + * If there is errors (virus found, antivir in error, bad filename), file is not moved. + * + * @param string $src_file Source full path filename ($_FILES['field']['tmp_name']) + * @param string $dest_file Target full path filename ($_FILES['field']['name']) + * @param int $allowoverwrite 1=Overwrite target file if it already exists + * @param int $disablevirusscan 1=Disable virus scan + * @param string $uploaderrorcode Value of upload error code ($_FILES['field']['error']) + * @param int $notrigger Disable all triggers + * @param string $upload_dir Upload directory + * @param array $upload_file Values of file uploaded $_FILES['field'] + * @return int >0 if OK, <0 or string if KO + */ +function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan=0, $uploaderrorcode=0, $notrigger=0, $upload_dir='', $upload_file=null) +{ + global $db, $hookmanager; + global $object; + + $error=0; + + // Check uploaded file + $dest_file=dolCheckUploadedFile($src_file, $dest_file, $disablevirusscan, $uploaderrorcode); + if (is_array($dest_file) && isset($dest_file['error'])) return $dest_file['error']; + + if (! is_object($hookmanager)) + { + if (! class_exists('HookManager')) { + // Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array + require DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($db); + } + } + $hookmanager->initHooks(array('fileslib')); + + $parameters=array('upload_dir' => $upload_dir, 'upload_file' => $upload_file, 'allowoverwrite' => $allowoverwrite); + $reshook=$hookmanager->executeHooks('dolMoveUploadedFile', $parameters, $object); + + if (empty($reshook)) { + return dolMoveUploadedFile($src_file, $dest_file, $allowoverwrite, $notrigger); + } +} + +/** + * Check an uploaded file. + * If there is errors (virus found, antivir in error, bad filename), file is not moved. + * + * @param string $src_file Source full path filename ($_FILES['field']['tmp_name']) + * @param string $dest_file Target full path filename ($_FILES['field']['name']) + * @param int $disablevirusscan 1=Disable virus scan + * @param string $uploaderrorcode Value of upload error code ($_FILES['field']['error']) + * @return int >0 if OK, <0 or string if KO + */ +function dolCheckUploadedFile($src_file, $dest_file, $disablevirusscan=0, $uploaderrorcode=0) +{ + global $conf; + + $file_name = $dest_file; + // If an upload error has been reported + if ($uploaderrorcode) + { + switch($uploaderrorcode) + { + case UPLOAD_ERR_INI_SIZE: // 1 + return array('error' => 'ErrorFileSizeTooLarge'); + break; + case UPLOAD_ERR_FORM_SIZE: // 2 + return array('error' => 'ErrorFileSizeTooLarge'); + break; + case UPLOAD_ERR_PARTIAL: // 3 + return array('error' => 'ErrorPartialFile'); + break; + case UPLOAD_ERR_NO_TMP_DIR: // + return array('error' => 'ErrorNoTmpDir'); + break; + case UPLOAD_ERR_CANT_WRITE: + return array('error' => 'ErrorFailedToWriteInDir'); + break; + case UPLOAD_ERR_EXTENSION: + return array('error' => 'ErrorUploadBlockedByAddon'); + break; + default: + break; + } + } + + // If we need to make a virus scan + if (empty($disablevirusscan) && file_exists($src_file) && ! empty($conf->global->MAIN_ANTIVIRUS_COMMAND)) + { + if (! class_exists('AntiVir')) { + require DOL_DOCUMENT_ROOT.'/core/class/antivir.class.php'; + } + $antivir=new AntiVir($db); + $result = $antivir->dol_avscan_file($src_file); + if ($result < 0) // If virus or error, we stop here + { + $reterrors=$antivir->errors; + dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$file_name.'") KO with antivirus: result='.$result.' errors='.join(',',$antivir->errors), LOG_WARNING); + return array('error' => 'ErrorFileIsInfectedWithAVirus: '.join(',',$reterrors)); + } + } + + // Security: + // Disallow file with some extensions. We renamed them. + // Car si on a mis le rep documents dans un rep de la racine web (pas bien), cela permet d'executer du code a la demande. + if (preg_match('/\.htm|\.html|\.php|\.pl|\.cgi$/i',$file_name)) + { + $file_name.= '.noexe'; + } + + // Security: + // On interdit fichiers caches, remontees de repertoire ainsi que les pipes dans les noms de fichiers. + if (preg_match('/^\./',$src_file) || preg_match('/\.\./',$src_file) || preg_match('/[<>|]/',$src_file)) + { + dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING); + return array('error' => -1); + } + + // Security: + // On interdit fichiers caches, remontees de repertoire ainsi que les pipe dans + // les noms de fichiers. + if (preg_match('/^\./',$dest_file) || preg_match('/\.\./',$dest_file) || preg_match('/[<>|]/',$dest_file)) + { + dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING); + return array('error' => -2); + } + + return $file_name; +} + /** * Move an uploaded file after some controls. * If there is errors (virus found, antivir in error, bad filename), file is not moved. @@ -560,89 +690,19 @@ function dol_unescapefile($filename) * @param string $src_file Source full path filename ($_FILES['field']['tmp_name']) * @param string $dest_file Target full path filename ($_FILES['field']['name']) * @param int $allowoverwrite 1=Overwrite target file if it already exists - * @param int $disablevirusscan 1=Disable virus scan - * @param string $uploaderrorcode Value of upload error code ($_FILES['field']['error']) * @param int $notrigger Disable all triggers * @return int >0 if OK, <0 or string if KO */ -function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan=0, $uploaderrorcode=0, $notrigger=0) +function dolMoveUploadedFile($src_file, $dest_file, $allowoverwrite, $notrigger=0) { global $conf, $user, $langs, $db; global $object; $error=0; - $file_name = $dest_file; - // If an upload error has been reported - if ($uploaderrorcode) - { - switch($uploaderrorcode) - { - case UPLOAD_ERR_INI_SIZE: // 1 - return 'ErrorFileSizeTooLarge'; - break; - case UPLOAD_ERR_FORM_SIZE: // 2 - return 'ErrorFileSizeTooLarge'; - break; - case UPLOAD_ERR_PARTIAL: // 3 - return 'ErrorPartialFile'; - break; - case UPLOAD_ERR_NO_TMP_DIR: // - return 'ErrorNoTmpDir'; - break; - case UPLOAD_ERR_CANT_WRITE: - return 'ErrorFailedToWriteInDir'; - break; - case UPLOAD_ERR_EXTENSION: - return 'ErrorUploadBlockedByAddon'; - break; - default: - break; - } - } - - // If we need to make a virus scan - if (empty($disablevirusscan) && file_exists($src_file) && ! empty($conf->global->MAIN_ANTIVIRUS_COMMAND)) - { - require_once DOL_DOCUMENT_ROOT.'/core/class/antivir.class.php'; - $antivir=new AntiVir($db); - $result = $antivir->dol_avscan_file($src_file); - if ($result < 0) // If virus or error, we stop here - { - $reterrors=$antivir->errors; - dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$file_name.'") KO with antivirus: result='.$result.' errors='.join(',',$antivir->errors), LOG_WARNING); - return 'ErrorFileIsInfectedWithAVirus: '.join(',',$reterrors); - } - } - - // Security: - // Disallow file with some extensions. We renamed them. - // Car si on a mis le rep documents dans un rep de la racine web (pas bien), cela permet d'executer du code a la demande. - if (preg_match('/\.htm|\.html|\.php|\.pl|\.cgi$/i',$file_name)) - { - $file_name.= '.noexe'; - } - - // Security: - // On interdit fichiers caches, remontees de repertoire ainsi que les pipes dans les noms de fichiers. - if (preg_match('/^\./',$src_file) || preg_match('/\.\./',$src_file) || preg_match('/[<>|]/',$src_file)) - { - dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING); - return -1; - } - - // Security: - // On interdit fichiers caches, remontees de repertoire ainsi que les pipe dans - // les noms de fichiers. - if (preg_match('/^\./',$dest_file) || preg_match('/\.\./',$dest_file) || preg_match('/[<>|]/',$dest_file)) - { - dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING); - return -2; - } - // The file functions must be in OS filesystem encoding. $src_file_osencoded=dol_osencode($src_file); - $file_name_osencoded=dol_osencode($file_name); + $file_name_osencoded=dol_osencode($dest_file); // Check if destination dir is writable // TODO @@ -973,11 +1033,11 @@ function dol_add_file_process($upload_dir,$allowoverwrite=0,$donotupdatesession= { global $db,$user,$conf,$langs,$_FILES; - if (! empty($_FILES[$varfiles]['tmp_name'])) + if (! empty($_FILES[$varfiles])) // For view $_FILES[$varfiles]['error'] { if (dol_mkdir($upload_dir) >= 0) { - $resupload = dol_move_uploaded_file($_FILES[$varfiles]['tmp_name'], $upload_dir . "/" . $_FILES[$varfiles]['name'],$allowoverwrite,0, $_FILES[$varfiles]['error']); + $resupload = dol_move_uploaded_file($_FILES[$varfiles]['tmp_name'], $upload_dir . "/" . $_FILES[$varfiles]['name'], $allowoverwrite, 0, $_FILES[$varfiles]['error'], 0, $upload_dir, $_FILES[$varfiles]); if (is_numeric($resupload) && $resupload > 0) { if (empty($donotupdatesession))