Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/classes/general/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/classes/general/file.php |
<?php /** * Bitrix Framework * @package bitrix * @subpackage main * @copyright 2001-2023 Bitrix */ use Bitrix\Main; use Bitrix\Main\IO; use Bitrix\Main\UI\Viewer; use Bitrix\Main\File; use Bitrix\Main\Web; use Bitrix\Main\Web\Uri; use Bitrix\Main\File\Image; use Bitrix\Main\File\Image\Rectangle; use Bitrix\Main\File\Internal; use Bitrix\Main\ORM\Query; use Bitrix\Main\Security; IncludeModuleLangFile(__FILE__); /** * @deprecated Use CFile * Class CAllFile */ class CAllFile { } class CFile extends CAllFile { protected const CACHE_DIR = 'b_file'; protected const DELETE_NONE = 0x00; protected const DELETE_FILE = 0x01; protected const DELETE_DB = 0x02; protected const DELETE_ALL = 0x03; public static function SaveForDB(&$arFields, $field, $strSavePath) { $arFile = $arFields[$field] ?? null; if(isset($arFile) && is_array($arFile)) { if( (isset($arFile["name"]) && $arFile["name"] <> '') || (isset($arFile["del"]) && $arFile["del"] <> '') || array_key_exists("description", $arFile) ) { $res = static::SaveFile($arFile, $strSavePath); if($res !== false) { $arFields[$field] = (intval($res) > 0? $res : false); return true; } } } unset($arFields[$field]); return false; } public static function checkForDb($arFields, $field) { if(isset($arFields[$field]) && is_array($arFields[$field])) { $arFile = $arFields[$field]; if($arFile["name"] == "") { return ""; } $fileName = self::transformName($arFile["name"]); return self::validateFile($fileName, $arFile); } else { return ""; } } protected static function transformName($name, $forceRandom = false, $bSkipExt = false) { //safe filename without path $fileName = GetFileName($name); $originalName = ($forceRandom != true && COption::GetOptionString("main", "save_original_file_name", "N") == "Y"); if($originalName) { //transforming original name: //transliteration if(COption::GetOptionString("main", "translit_original_file_name", "N") == "Y") { $fileName = CUtil::translit($fileName, LANGUAGE_ID, array( "max_len" => 1024, "safe_chars" => ".", "replace_space" => '-', "change_case" => false, )); } //replace invalid characters if(COption::GetOptionString("main", "convert_original_file_name", "Y") == "Y") { $io = CBXVirtualIo::GetInstance(); $fileName = $io->RandomizeInvalidFilename($fileName); } } //.jpe is not image type on many systems if($bSkipExt == false && strtolower(GetFileExtension($fileName)) == "jpe") { $fileName = substr($fileName, 0, -4).".jpg"; } //double extension vulnerability $fileName = RemoveScriptExtension($fileName); if(!$originalName) { //name is randomly generated $fileName = Security\Random::getString(32).($bSkipExt == true || ($ext = GetFileExtension($fileName)) == ''? '' : ".".$ext); } return $fileName; } protected static function validateFile($strFileName, $arFile) { if($strFileName == '') return GetMessage("FILE_BAD_FILENAME"); $io = CBXVirtualIo::GetInstance(); if(!$io->ValidateFilenameString($strFileName)) return GetMessage("MAIN_BAD_FILENAME1"); if(mb_strlen($strFileName) > 255) return GetMessage("MAIN_BAD_FILENAME_LEN"); //check .htaccess etc. if(IsFileUnsafe($strFileName)) return GetMessage("FILE_BAD_TYPE"); //nginx returns octet-stream for .jpg if(GetFileNameWithoutExtension($strFileName) == '') return GetMessage("FILE_BAD_FILENAME"); if (COption::GetOptionInt("main", "disk_space") > 0) { $quota = new CDiskQuota(); if (!$quota->checkDiskQuota($arFile)) return GetMessage("FILE_BAD_QUOTA"); } return ""; } public static function SaveFile($arFile, $strSavePath, $forceRandom = false, $skipExtension = false, $dirAdd = '') { $strFileName = GetFileName($arFile["name"] ?? ''); /* filename.gif */ if(isset($arFile["del"]) && $arFile["del"] <> '') { static::Delete($arFile["old_file"] ?? 0); if($strFileName == '') return "NULL"; } if(!isset($arFile["name"]) || $arFile["name"] == '') { if(isset($arFile["description"]) && intval($arFile["old_file"])>0) { static::UpdateDesc($arFile["old_file"], $arFile["description"]); } return false; } if (isset($arFile["content"])) { if (!isset($arFile["size"])) { $arFile["size"] = strlen($arFile["content"]); } } else { try { $file = new IO\File(IO\Path::convertPhysicalToLogical($arFile["tmp_name"])); $arFile["size"] = $file->getSize(); } catch(IO\IoException $e) { if (!isset($arFile["size"]) || !is_int($arFile["size"])) { $arFile["size"] = 0; } } } $arFile["ORIGINAL_NAME"] = $strFileName; //translit, replace unsafe chars, etc. $strFileName = self::transformName($strFileName, $forceRandom, $skipExtension); //transformed name must be valid, check disk quota, etc. if (self::validateFile($strFileName, $arFile) !== "") { return false; } $arFile["type"] = Web\MimeType::normalize($arFile["type"]); $original = null; $io = CBXVirtualIo::GetInstance(); $bExternalStorage = false; foreach(GetModuleEvents("main", "OnFileSave", true) as $arEvent) { if(ExecuteModuleEventEx($arEvent, array(&$arFile, $strFileName, $strSavePath, $forceRandom, $skipExtension, $dirAdd))) { $bExternalStorage = true; break; } } if(!$bExternalStorage) { // we should keep number of files in a folder below 10,000 // three chars from md5 give us 4096 subdirs $upload_dir = COption::GetOptionString("main", "upload_dir", "upload"); if($forceRandom != true && COption::GetOptionString("main", "save_original_file_name", "N") == "Y") { //original name $subdir = $dirAdd; if($subdir == '') { while(true) { $random = Security\Random::getString(32); $subdir = substr(md5($random), 0, 3)."/".$random; if(!$io->FileExists($_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$strSavePath."/".$subdir."/".$strFileName)) { break; } } } $strSavePath = rtrim($strSavePath, "/")."/".$subdir; } else { //random name $fileExtension = ($skipExtension == true || ($ext = GetFileExtension($strFileName)) == ''? '' : ".".$ext); while(true) { $subdir = substr(md5($strFileName), 0, 3); $strSavePath = rtrim($strSavePath, "/")."/".$subdir; if(!$io->FileExists($_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$strSavePath."/".$strFileName)) { break; } //try the new name $strFileName = Security\Random::getString(32).$fileExtension; } } $arFile["SUBDIR"] = $strSavePath; $arFile["FILE_NAME"] = $strFileName; $dirName = $_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$strSavePath."/"; $physicalFileName = $io->GetPhysicalName($dirName.$strFileName); CheckDirPath($dirName); if(is_set($arFile, "content")) { if(file_put_contents($physicalFileName, $arFile["content"]) === false) { return false; } } else { if(!copy($arFile["tmp_name"], $physicalFileName) && !move_uploaded_file($arFile["tmp_name"], $physicalFileName)) { return false; } } if(isset($arFile["old_file"])) { static::Delete($arFile["old_file"]); } @chmod($physicalFileName, BX_FILE_PERMISSIONS); //flash is not an image $flashEnabled = !static::IsImage($arFile["ORIGINAL_NAME"], $arFile["type"]); $image = new File\Image($physicalFileName); $imgInfo = $image->getInfo($flashEnabled); if($imgInfo) { $arFile["WIDTH"] = $imgInfo->getWidth(); $arFile["HEIGHT"] = $imgInfo->getHeight(); if ($imgInfo->getFormat() == File\Image::FORMAT_JPEG && empty($arFile['no_rotate']) && !$image->exceedsMaxSize()) { $exifData = $image->getExifData(); if (isset($exifData['Orientation']) && $exifData['Orientation'] > 1) { if($image->load()) { if($image->autoRotate($exifData['Orientation'])) { $quality = COption::GetOptionString('main', 'image_resize_quality'); if($image->save($quality)) { // update width and height $arFile['WIDTH'] = $image->getWidth(); $arFile['HEIGHT'] = $image->getHeight(); $arFile['size'] = filesize($physicalFileName); } } } } } } else { $arFile["WIDTH"] = 0; $arFile["HEIGHT"] = 0; } //calculate a hash for the control of duplicates $arFile["FILE_HASH"] = static::CalculateHash($physicalFileName, $arFile["size"]); //control of duplicates if ($arFile["FILE_HASH"] <> '') { $lockId = static::lockFileHash($arFile["size"], $arFile["FILE_HASH"]); $original = static::FindDuplicate($arFile["size"], $arFile["FILE_HASH"]); if($original !== null) { //points to the original's physical path $arFile["SUBDIR"] = $original->getFile()->getSubdir(); $arFile["FILE_NAME"] = $original->getFile()->getFileName(); $originalPath = $_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$arFile["SUBDIR"]."/".$arFile["FILE_NAME"]; if($physicalFileName <> $io->GetPhysicalName($originalPath)) { unlink($physicalFileName); try { rmdir($io->GetPhysicalName($dirName)); } catch (\ErrorException $exception) { // Ignore a E_WARNING Error } } } } } else { //from clouds if(isset($arFile["original_file"]) && $arFile["original_file"] instanceof Internal\EO_FileHash) { $original = $arFile["original_file"]; } } if($arFile["WIDTH"] == 0 || $arFile["HEIGHT"] == 0) { //mock image because we got false from CFile::GetImageSize() if(strpos($arFile["type"], "image/") === 0 && $arFile["type"] <> 'image/svg+xml') { $arFile["type"] = "application/octet-stream"; } } /****************************** QUOTA ******************************/ if (COption::GetOptionInt("main", "disk_space") > 0 && $original === null) { CDiskQuota::updateDiskQuota("file", $arFile["size"], "insert"); } /****************************** QUOTA ******************************/ $NEW_IMAGE_ID = static::DoInsert(array( "HEIGHT" => $arFile["HEIGHT"], "WIDTH" => $arFile["WIDTH"], "FILE_SIZE" => $arFile["size"], "CONTENT_TYPE" => $arFile["type"], "SUBDIR" => $arFile["SUBDIR"], "FILE_NAME" => $arFile["FILE_NAME"], "MODULE_ID" => $arFile["MODULE_ID"] ?? '', "ORIGINAL_NAME" => $arFile["ORIGINAL_NAME"], "DESCRIPTION" => ($arFile["description"] ?? ''), "HANDLER_ID" => ($arFile["HANDLER_ID"] ?? ''), "EXTERNAL_ID" => ($arFile["external_id"] ?? md5(mt_rand())), "FILE_HASH" => ($original === null? $arFile["FILE_HASH"] : ''), )); if (isset($lockId)) { static::unlockFileHash($lockId); } if($original !== null) { //save information about the duplicate for future use (on deletion) static::AddDuplicate($original->getFileId(), $NEW_IMAGE_ID, false); } static::CleanCache($NEW_IMAGE_ID); return $NEW_IMAGE_ID; } /** * Calculates a hash of the file. * @param string $file Full path to the file. * @param int $size Size of the file. * @return string */ protected static function CalculateHash($file, $size) { $hash = ''; if ($size > 0 && COption::GetOptionString('main', 'control_file_duplicates', 'N') === 'Y') { $maxSize = (int)COption::GetOptionString('main', 'duplicates_max_size', '100') * 1024 * 1024; //Mbytes if($size <= $maxSize || $maxSize === 0) { $hash = hash_file("md5", $file); } } return $hash; } /** * @param int $size * @param string $hash * @param int|null $handlerId * @return Internal\EO_FileHash|null */ public static function FindDuplicate($size, $hash, $handlerId = null) { $filter = Query\Query::filter() ->where("FILE_SIZE", $size) ->where("FILE_HASH", $hash); if($handlerId !== null) { $filter->where("FILE.HANDLER_ID", $handlerId); } else { $filter->where(Query\Query::filter() ->logic('or') ->where('FILE.HANDLER_ID', '') ->whereNull('FILE.HANDLER_ID') ); } return Internal\FileHashTable::query() ->addSelect("FILE.*") ->where($filter) ->addOrder("FILE_ID") ->setLimit(1) ->fetchObject(); } /** * Adds information about a duplicate file. * For internal use only. * * @param int $originalId Original file ID. * @param int|null $duplicateId Duplicate file ID (optional if the original and duplicate files are the same). * @param bool $resolvePossibleOriginCycle Check if the desired original file is already in the table and * if it's a duplicate of another file, then use the real original file ID from the table. * * @internal */ public static function AddDuplicate($originalId, $duplicateId = null, bool $resolvePossibleOriginCycle = true) { if($duplicateId === null) { $duplicateId = $originalId; } if($resolvePossibleOriginCycle || $originalId == $duplicateId) { //possibly there is the original already for the file $original = Internal\FileDuplicateTable::query() ->addSelect("ORIGINAL_ID") ->where("DUPLICATE_ID", $originalId) ->fetch(); if($original) { $originalId = $original["ORIGINAL_ID"]; } } $updateFields = [ "COUNTER" => new Main\DB\SqlExpression(Internal\FileDuplicateTable::getTableName() . '.?# + 1', 'COUNTER'), ]; $insertFields = [ "DUPLICATE_ID" => $duplicateId, "ORIGINAL_ID" => $originalId, ]; Internal\FileDuplicateTable::merge($insertFields, $updateFields); } /** * Adds information about a duplicate file. * @param int $originalId Original file ID. * @param array $duplicteIds List of file IDs to delete and update their path to the original file path. */ public static function DeleteDuplicates($originalId, array $duplicteIds) { $connection = \Bitrix\Main\Application::getConnection(); $helper = $connection->getSqlHelper(); $original = Internal\FileHashTable::getList([ 'select' => ['FILE_SIZE', 'FILE_HASH', 'FILE.*'], 'filter' => ['=FILE_ID' => $originalId], ])->fetchObject(); if (!$original) { return; } $originalPath = '/' . $original->getFile()->getSubdir() . '/' . $original->getFile()->getFileName(); $io = CBXVirtualIo::GetInstance(); $uploadDir = COption::GetOptionString("main", "upload_dir", "upload"); $deleteSize = 0; $lockId = ''; $fileList = \Bitrix\Main\FileTable::getList([ 'select' => ['ID', 'FILE_SIZE', 'SUBDIR', 'FILE_NAME'], 'filter' => [ '=ID' => $duplicteIds, '=HANDLER_ID' => $original->getFile()->getHandlerId(), ], 'order' => [ 'ID' => 'ASC', ], ]); while ($duplicate = $fileList->fetchObject()) { if (!$lockId) { $lockId = static::lockFileHash($original->getFileSize(), $original->getFileHash()); } $deleteResult = Internal\FileHashTable::delete($duplicate->getId()); $duplicatePath = '/' . $duplicate->getSubdir() . '/' . $duplicate->getFileName(); if ($originalPath == $duplicatePath) { continue; } $cancel = false; foreach (GetModuleEvents('main', 'OnBeforeFileDeleteDuplicate', true) as $event) { $cancel = ExecuteModuleEventEx($event, array($original->getFile(), $duplicate)); if ($cancel) { break; } } if ($cancel) { continue; } static::AddDuplicate($originalId, $duplicate->getId(), false); $update = $helper->prepareUpdate('b_file', [ 'SUBDIR' => $original->getFile()->getSubdir(), 'FILE_NAME' => $original->getFile()->getFileName(), ]); $ddl = 'UPDATE b_file SET ' . $update[0] . 'WHERE ID = ' . $duplicate->getId(); $connection->queryExecute($ddl); static::cleanCache($duplicate->getId()); $isExternal = false; foreach (GetModuleEvents('main', 'OnAfterFileDeleteDuplicate', true) as $event) { $isExternal = ExecuteModuleEventEx($event, array($original->getFile(), $duplicate)) || $isExternal; } if (!$isExternal) { $dname = $_SERVER["DOCUMENT_ROOT"] . '/' . $uploadDir . '/' . $duplicate->getSubdir(); $fname = $dname . '/' . $duplicate->getFileName(); $file = $io->GetFile($fname); if ($file->isExists() && $file->unlink()) { $deleteSize += $duplicate->getFileSize(); } $directory = $io->GetDirectory($dname); if ($directory->isExists() && $directory->isEmpty()) { if ($directory->rmdir()) { $parent = $io->GetDirectory(GetDirPath($dname)); if ($parent->isExists() && $parent->isEmpty()) { $parent->rmdir(); } } } } } if ($lockId) { static::unlockFileHash($lockId); } /****************************** QUOTA ******************************/ if ($deleteSize > 0 && COption::GetOptionInt("main", "disk_space") > 0) { CDiskQuota::updateDiskQuota("file", $deleteSize, "delete"); } /****************************** QUOTA ******************************/ } public static function CloneFile(int $fileId): ?int { $originalFile = static::GetByID($fileId)->Fetch(); if (!$originalFile) { return null; } $originalFile['FILE_HASH'] = ''; $cloneId = static::DoInsert($originalFile); static::AddDuplicate($fileId, $cloneId); static::CleanCache($cloneId); return $cloneId; } public static function DoInsert($arFields) { global $DB; $size = round(floatval($arFields["FILE_SIZE"])); $strSql = "INSERT INTO b_file( TIMESTAMP_X ,MODULE_ID ,HEIGHT ,WIDTH ,FILE_SIZE ,CONTENT_TYPE ,SUBDIR ,FILE_NAME ,ORIGINAL_NAME ,DESCRIPTION ,HANDLER_ID ,EXTERNAL_ID ) VALUES ( ".$DB->GetNowFunction()." ,'".$DB->ForSQL($arFields["MODULE_ID"], 50)."' ,".intval($arFields["HEIGHT"])." ,".intval($arFields["WIDTH"])." ,".$size." ,'".$DB->ForSql($arFields["CONTENT_TYPE"], 255)."' ,'".$DB->ForSql($arFields["SUBDIR"], 255)."' ,'".$DB->ForSQL($arFields["FILE_NAME"], 255)."' ,'".$DB->ForSql($arFields["ORIGINAL_NAME"], 255)."' ,'".$DB->ForSQL($arFields["DESCRIPTION"], 255)."' ,".($arFields["HANDLER_ID"]? "'".$DB->ForSql($arFields["HANDLER_ID"], 50)."'": "null")." ,".($arFields["EXTERNAL_ID"] != ""? "'".$DB->ForSql($arFields["EXTERNAL_ID"], 50)."'": "null")." )"; $DB->Query($strSql); $fileId = $DB->LastID(); //store the file hash for duplicates search if($arFields["FILE_HASH"] <> '') { Internal\FileHashTable::add([ "FILE_ID" => $fileId, "FILE_SIZE" => $size, "FILE_HASH" => $arFields["FILE_HASH"], ]); } $arFields["ID"] = $fileId; foreach(GetModuleEvents("main", "OnAfterFileSave", true) as $arEvent) { ExecuteModuleEventEx($arEvent, array($arFields)); } return $fileId; } public static function Delete($ID) { $ID = intval($ID); if($ID <= 0) return; $conn = Main\Application::getConnection(); $res = static::GetByID($ID, true); if($res = $res->Fetch()) { $hash = Internal\FileHashTable::getRowById($ID); $lockId = $hash ? static::lockFileHash($hash['FILE_SIZE'], $hash['FILE_HASH'], $res['HANDLER_ID']) : ''; $delete = static::processDuplicates($ID); if($delete === self::DELETE_NONE) { //can't delete the file - duplicates found return; } $delete_size = 0; if($delete & self::DELETE_FILE) { $upload_dir = COption::GetOptionString("main", "upload_dir", "upload"); $dname = $_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$res["SUBDIR"]; $fname = $dname."/".$res["FILE_NAME"]; $io = CBXVirtualIo::GetInstance(); $file = $io->GetFile($fname); if($file->isExists() && $file->unlink()) { $delete_size += $res["FILE_SIZE"]; } $delete_size += static::ResizeImageDelete($res); $directory = $io->GetDirectory($dname); if($directory->isExists() && $directory->isEmpty()) { if ($directory->rmdir()) { $parent = $io->GetDirectory(GetDirPath($dname)); if ($parent->isExists() && $parent->isEmpty()) { $parent->rmdir(); } } } foreach(GetModuleEvents("main", "OnPhysicalFileDelete", true) as $arEvent) { ExecuteModuleEventEx($arEvent, array($res)); } } if($delete & self::DELETE_DB) { foreach(GetModuleEvents("main", "OnFileDelete", true) as $arEvent) { ExecuteModuleEventEx($arEvent, array($res)); } Internal\FileHashTable::delete($ID); // recursion inside static::processVersions($ID); $conn->query("DELETE FROM b_file WHERE ID = {$ID}"); static::CleanCache($ID); } if ($lockId) { static::unlockFileHash($lockId); } /****************************** QUOTA ******************************/ if($delete_size > 0 && COption::GetOptionInt("main", "disk_space") > 0) CDiskQuota::updateDiskQuota("file", $delete_size, "delete"); /****************************** QUOTA ******************************/ } } public static function lockFileHash($size, $hash, $bucket = 0) { $lockId = $size . '|' . $hash . '|' . (int)$bucket; Main\Application::getConnection()->lock($lockId, -1); return $lockId; } public static function unlockFileHash($lockId) { Main\Application::getConnection()->unlock($lockId); } protected static function processDuplicates($ID) { $result = self::DELETE_ALL; //Part 1: the file is a duplicate of another file, including referenses to itself $original = Internal\FileDuplicateTable::query() ->addSelect("*") ->where("DUPLICATE_ID", $ID) ->fetch(); //Part 2: find duplicates of the file $duplicates = Internal\FileDuplicateTable::query() ->where("ORIGINAL_ID", $ID) ->setLimit(1) ->fetch(); //Part 1 if($original) { if($original["COUNTER"] > 1) { //decrease references counter Internal\FileDuplicateTable::update( [ "DUPLICATE_ID" => $ID, "ORIGINAL_ID" => $original["ORIGINAL_ID"], ], [ "COUNTER" => new Main\DB\SqlExpression("?# - 1", "COUNTER"), ] ); //there are references still $result = self::DELETE_NONE; } else { //delete referense Internal\FileDuplicateTable::delete([ "DUPLICATE_ID" => $ID, "ORIGINAL_ID" => $original["ORIGINAL_ID"], ]); //delete only if the file is a duplicate of *another* file if($original["DUPLICATE_ID"] <> $original["ORIGINAL_ID"]) { if($original["ORIGINAL_DELETED"] === "Y") { //try and delete the original static::Delete($original["ORIGINAL_ID"]); } //there is the original somewhere, we shouldn't delete its file $result = self::DELETE_DB; } } } //Part 2 if($duplicates) { //mark the original as deleted for future deletion Internal\FileDuplicateTable::markDeleted($ID); //duplicates found, should keep the original $result = self::DELETE_NONE; } return $result; } /** * Adds information about a version of a file. * @param int $originalId Original file ID. * @param int $versionId Version file ID. * @param array $metaData The version peculiarities. */ public static function AddVersion($originalId, $versionId, $metaData = []) { $result = Internal\FileVersionTable::add([ 'ORIGINAL_ID' => $originalId, 'VERSION_ID' => $versionId, ] + (empty($metaData) ? [] : [ 'META' => $metaData ])); static::CleanCache($originalId); return $result; } protected static function processVersions($ID) { // check if the file is something's version $original = Internal\FileVersionTable::query() ->addSelect('*') ->where('VERSION_ID', $ID) ->fetch() ; if ($original) { Internal\FileVersionTable::delete(['ORIGINAL_ID' => $original['ORIGINAL_ID']]); static::CleanCache($original['ORIGINAL_ID']); } // check if the file has versions $versions = Internal\FileVersionTable::query() ->addSelect('*') ->where('ORIGINAL_ID', $ID) ->exec() ; while ($version = $versions->fetch()) { static::Delete($version['VERSION_ID']); } } /** * @deprecated Use CFile::Delete() * @param $ID */ public static function DoDelete($ID) { static::Delete($ID); } public static function CleanCache($fileId) { if (CACHED_b_file !== false) { $bucket_size = (int)CACHED_b_file_bucket_size; if ($bucket_size <= 0) { $bucket_size = 10; } $bucket = (int)($fileId / $bucket_size); $cache = Main\Application::getInstance()->getManagedCache(); $cache->clean(self::CACHE_DIR . '01' . $bucket, self::CACHE_DIR); $cache->clean(self::CACHE_DIR . '11' . $bucket, self::CACHE_DIR); $cache->clean(self::CACHE_DIR . '00' . $bucket, self::CACHE_DIR); $cache->clean(self::CACHE_DIR . '10' . $bucket, self::CACHE_DIR); } } public static function GetFromCache($fileId, $realId = false) { global $DB; $cache = Main\Application::getInstance()->getManagedCache(); $bucketSize = (int)CACHED_b_file_bucket_size; if ($bucketSize <= 0) { $bucketSize = 10; } $bucket = (int)($fileId / $bucketSize); $https = (int)Main\Context::getCurrent()->getRequest()->isHttps(); $cacheId = self::CACHE_DIR . $https . (int)$realId . $bucket; if ($cache->read(CACHED_b_file, $cacheId, self::CACHE_DIR)) { $files = $cache->get($cacheId); if (!isset($files[$fileId])) { // the trail of an incomplete bucket if (!is_array($files)) { $files = []; } if ($file = static::GetFromDb($fileId, $realId)->Fetch()) { $files[$fileId] = $file; static::CleanCache($fileId); } } } else { $files = []; $minId = $bucket * $bucketSize; $maxId = ($bucket + 1) * $bucketSize - 1; $sql = " SELECT f.*, {$DB->DateToCharFunction("f.TIMESTAMP_X")} as TIMESTAMP_X, NULL as VERSION_ORIGINAL_ID, '' as META FROM b_file f WHERE f.ID >= {$minId} AND f.ID <= {$maxId} "; if ($realId !== true) { $sql .= " UNION SELECT f.*, {$DB->DateToCharFunction("f.TIMESTAMP_X")} as TIMESTAMP_X, fv.ORIGINAL_ID as VERSION_ORIGINAL_ID, fv.META as META FROM b_file f INNER JOIN b_file_version fv ON fv.VERSION_ID = f.ID WHERE fv.ORIGINAL_ID >= {$minId} AND fv.ORIGINAL_ID <= {$maxId} ORDER BY ID "; } $rs = $DB->Query($sql); while ($file = $rs->fetch()) { $originalId = ($file['VERSION_ORIGINAL_ID'] ?: $file["ID"]); $files[$originalId] = $file; } // store SRC in cache foreach ($files as $id => $file) { $files[$id]['SRC'] = static::GetFileSRC($file); } $cache->setImmediate($cacheId, $files); } return $files; } public static function GetByID($fileId, $realId = false) { $fileId = (int)$fileId; if (CACHED_b_file === false) { $result = static::GetFromDb($fileId, $realId); } else { $files = static::GetFromCache($fileId, $realId); $result = new CDBResult; $result->InitFromArray(isset($files[$fileId]) ? [$files[$fileId]] : []); } return $result; } protected static function GetFromDb($fileId, $realId) { global $DB; $strSql = " SELECT f.*, {$DB->DateToCharFunction("f.TIMESTAMP_X")} as TIMESTAMP_X, NULL as VERSION_ORIGINAL_ID, '' as META FROM b_file f WHERE f.ID = {$fileId} "; if ($realId !== true) { $strSql .= " UNION SELECT f.*, {$DB->DateToCharFunction("f.TIMESTAMP_X")} as TIMESTAMP_X, fv.ORIGINAL_ID as VERSION_ORIGINAL_ID, fv.META as META FROM b_file f INNER JOIN b_file_version fv ON fv.VERSION_ID = f.ID WHERE fv.ORIGINAL_ID = {$fileId} ORDER BY ID DESC LIMIT 1 "; } return $DB->Query($strSql); } public static function GetList($arOrder = array(), $arFilter = array()) { global $DB; $arSqlSearch = array(); $arSqlOrder = array(); $strSqlSearch = ""; if(is_array($arFilter)) { foreach($arFilter as $key => $val) { $key = strtoupper($key); $strOperation = ''; if(substr($key, 0, 1) == "@") { $key = substr($key, 1); $strOperation = "IN"; $arIn = is_array($val)? $val: explode(',', $val); $val = ''; foreach($arIn as $v) { $val .= ($val <> ''? ',':'')."'".$DB->ForSql(trim($v))."'"; } } else { $val = $DB->ForSql($val); } if($val == '') continue; switch($key) { case "MODULE_ID": case "ID": case "EXTERNAL_ID": case "SUBDIR": case "FILE_NAME": case "ORIGINAL_NAME": case "CONTENT_TYPE": case "HANDLER_ID": if ($strOperation == "IN") $arSqlSearch[] = "f.".$key." IN (".$val.")"; else $arSqlSearch[] = "f.".$key." = '".$val."'"; break; } } } if(!empty($arSqlSearch)) $strSqlSearch = " WHERE (".implode(") AND (", $arSqlSearch).")"; if(is_array($arOrder)) { static $aCols = array( "ID" => 1, "TIMESTAMP_X" => 1, "MODULE_ID" => 1, "HEIGHT" => 1, "WIDTH" => 1, "FILE_SIZE" => 1, "CONTENT_TYPE" => 1, "SUBDIR" => 1, "FILE_NAME" => 1, "ORIGINAL_NAME" => 1, "EXTERNAL_ID" => 1, ); foreach($arOrder as $by => $ord) { $by = strtoupper($by); if(array_key_exists($by, $aCols)) $arSqlOrder[] = "f.".$by." ".(strtoupper($ord) == "DESC"? "DESC":"ASC"); } } if(empty($arSqlOrder)) $arSqlOrder[] = "f.ID ASC"; $strSqlOrder = " ORDER BY ".implode(", ", $arSqlOrder); $strSql = "SELECT f.*, ".$DB->DateToCharFunction("f.TIMESTAMP_X")." as TIMESTAMP_X ". "FROM b_file f ". $strSqlSearch. $strSqlOrder; $res = $DB->Query($strSql); return $res; } public static function GetFileSRC($file, $uploadDir = false, $external = true) { $src = ''; if ($external) { foreach(GetModuleEvents('main', 'OnGetFileSRC', true) as $event) { $src = ExecuteModuleEventEx($event, [$file]); if ($src) { break; } } } if (!$src) { if ($uploadDir === false) { $uploadDir = COption::GetOptionString('main', 'upload_dir', 'upload'); } $src = '/' . $uploadDir . '/' . $file['SUBDIR'] . '/' . $file['FILE_NAME']; $src = str_replace('//', '/', $src); if (defined("BX_IMG_SERVER")) { $src = BX_IMG_SERVER . $src; } } return $src; } public static function GetFileArray($fileId, $uploadDir = false) { if (!is_array($fileId) && intval($fileId) > 0) { $file = static::GetByID($fileId)->Fetch(); if ($file) { if (!isset($file['SRC']) || $uploadDir !== false) { $file['SRC'] = static::GetFileSRC($file, $uploadDir); } return $file; } } return false; } public static function ConvertFilesToPost($source, &$target, $field=false) { if($field === false) { foreach($source as $field => $sub_source) { self::ConvertFilesToPost($sub_source, $target, $field); } } else { foreach($source as $id => $sub_source) { if(!array_key_exists($id, $target)) $target[$id] = array(); if(is_array($sub_source)) self::ConvertFilesToPost($sub_source, $target[$id], $field); else $target[$id][$field] = $sub_source; } } } /** * @deprecated Consider using \CFile::CloneFile(). * @see \CFile::CloneFile() */ public static function CopyFile($FILE_ID, $bRegister = true, $newPath = "") { $z = static::GetByID($FILE_ID); if($zr = $z->Fetch()) { /****************************** QUOTA ******************************/ if (COption::GetOptionInt("main", "disk_space") > 0) { $quota = new CDiskQuota(); if (!$quota->checkDiskQuota($zr)) return false; } /****************************** QUOTA ******************************/ $strNewFile = ''; $bSaved = false; $bExternalStorage = false; foreach(GetModuleEvents("main", "OnFileCopy", true) as $arEvent) { if($bSaved = ExecuteModuleEventEx($arEvent, array(&$zr, $newPath))) { $bExternalStorage = true; break; } } $io = CBXVirtualIo::GetInstance(); if(!$bExternalStorage) { $strDirName = $_SERVER["DOCUMENT_ROOT"]."/".(COption::GetOptionString("main", "upload_dir", "upload")); $strDirName = rtrim(str_replace("//","/",$strDirName), "/"); $zr["SUBDIR"] = trim($zr["SUBDIR"], "/"); $zr["FILE_NAME"] = ltrim($zr["FILE_NAME"], "/"); $strOldFile = $strDirName."/".$zr["SUBDIR"]."/".$zr["FILE_NAME"]; if($newPath <> '') { $strNewFile = $strDirName."/".ltrim($newPath, "/"); } else { $strNewFile = $strDirName."/".$zr["SUBDIR"]."/".md5(uniqid(mt_rand())).strrchr($zr["FILE_NAME"], "."); } $zr["FILE_NAME"] = bx_basename($strNewFile); $zr["SUBDIR"] = mb_substr($strNewFile, mb_strlen($strDirName) + 1, -(mb_strlen(bx_basename($strNewFile)) + 1)); if($newPath <> '') { CheckDirPath($strNewFile); } $bSaved = copy($io->GetPhysicalName($strOldFile), $io->GetPhysicalName($strNewFile)); } if($bSaved) { if($bRegister) { $NEW_FILE_ID = static::DoInsert($zr); if (COption::GetOptionInt("main", "disk_space") > 0) CDiskQuota::updateDiskQuota("file", $zr["FILE_SIZE"], "copy"); static::CleanCache($NEW_FILE_ID); return $NEW_FILE_ID; } else { if(!$bExternalStorage) return mb_substr($strNewFile, mb_strlen(rtrim($_SERVER["DOCUMENT_ROOT"], "/"))); else return $bSaved; } } else { return false; } } return 0; } public static function UpdateDesc($ID, $desc) { global $DB; $DB->Query( "UPDATE b_file SET DESCRIPTION = '".$DB->ForSql($desc, 255)."', TIMESTAMP_X = ".$DB->GetNowFunction()." WHERE ID=".intval($ID) ); static::CleanCache($ID); } public static function UpdateExternalId($ID, $external_id) { global $DB; $external_id = trim($external_id); $DB->Query( "UPDATE b_file SET EXTERNAL_ID = ".($external_id != ""? "'".$DB->ForSql($external_id, 50)."'": "null").", TIMESTAMP_X = ".$DB->GetNowFunction()." WHERE ID=".intval($ID) ); static::CleanCache($ID); } public static function InputFile($strFieldName, $int_field_size, $strImageID, $strImageStorePath=false, $int_max_file_size=0, $strFileType="IMAGE", $field_file="class=typefile", $description_size=0, $field_text="class=typeinput", $field_checkbox="", $bShowNotes = true, $bShowFilePath = true) { $strReturn1 = ""; if($int_max_file_size != 0) $strReturn1 .= "<input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"".$int_max_file_size."\" /> "; $strReturn1 .= ' <input name="'.$strFieldName.'" '.$field_file.' size="'.$int_field_size.'" type="file" />'; $strReturn2 = '<span class="bx-input-file-desc">'; $strDescription = ""; $db_img_arr = static::GetFileArray($strImageID, $strImageStorePath); if($db_img_arr) { $strDescription = $db_img_arr["DESCRIPTION"]; if(($p = mb_strpos($strFieldName, "["))>0) { $strDelName = mb_substr($strFieldName, 0, $p)."_del".mb_substr($strFieldName, $p); } else { $strDelName = $strFieldName."_del"; } if($bShowNotes) { if($bShowFilePath) { $filePath = $db_img_arr["SRC"]; } else { $filePath = $db_img_arr['ORIGINAL_NAME']; } $io = CBXVirtualIo::GetInstance(); if($io->FileExists($_SERVER["DOCUMENT_ROOT"].$db_img_arr["SRC"]) || $db_img_arr["HANDLER_ID"]) { $strReturn2 .= "<br> ".GetMessage("FILE_TEXT").": ".htmlspecialcharsEx($filePath); if(mb_strtoupper($strFileType) == "IMAGE") { $intWidth = intval($db_img_arr["WIDTH"]); $intHeight = intval($db_img_arr["HEIGHT"]); if($intWidth>0 && $intHeight>0) { $strReturn2 .= "<br> ".GetMessage("FILE_WIDTH").": $intWidth"; $strReturn2 .= "<br> ".GetMessage("FILE_HEIGHT").": $intHeight"; } } $strReturn2 .= "<br> ".GetMessage("FILE_SIZE").": ".static::FormatSize($db_img_arr["FILE_SIZE"]); } else { $strReturn2 .= "<br>".GetMessage("FILE_NOT_FOUND").": ".htmlspecialcharsEx($filePath); } } $strReturn2 .= "<br><input ".$field_checkbox." type=\"checkbox\" name=\"".$strDelName."\" value=\"Y\" id=\"".$strDelName."\" /> <label for=\"".$strDelName."\">".GetMessage("FILE_DELETE")."</label>"; } $strReturn2 .= '</span>'; return $strReturn1.( $description_size > 0? '<br><input type="text" value="'.htmlspecialcharsbx($strDescription).'" name="'.$strFieldName.'_descr" '.$field_text.' size="'.$description_size.'" title="'.GetMessage("MAIN_FIELD_FILE_DESC").'" />' :'' ).$strReturn2; } /** * @param float $size * @param int $precision * @return string */ public static function FormatSize($size, $precision = 2) { static $a = array("b", "Kb", "Mb", "Gb", "Tb"); $size = (float)$size; $pos = 0; while($size >= 1024 && $pos < 4) { $size /= 1024; $pos++; } return round($size, $precision)." ".GetMessage("FILE_SIZE_".$a[$pos]); } public static function GetImageExtensions() { return "jpg,bmp,jpeg,jpe,gif,png,webp"; } public static function GetFlashExtensions() { return "swf"; } public static function IsImage($filename, $mime_type=false) { $ext = strtolower(GetFileExtension($filename)); if($ext <> '') { if(in_array($ext, explode(",", static::GetImageExtensions()))) { if($mime_type === false || Web\MimeType::isImage($mime_type)) { return true; } } } return false; } public static function CheckImageFile($arFile, $iMaxSize=0, $iMaxWidth=0, $iMaxHeight=0, $access_typies=array(), $bForceMD5=false, $bSkipExt=false) { if (!isset($arFile["name"]) || $arFile["name"] == "") { return ""; } if (empty($arFile["tmp_name"])) { return GetMessage("FILE_BAD_FILE_TYPE").".<br>"; } if (preg_match("#^(php://|phar://)#i", $arFile["tmp_name"])) { return GetMessage("FILE_BAD_FILE_TYPE").".<br>"; } $file_type = GetFileType($arFile["name"]); // IMAGE by default $flashEnabled = false; if(!in_array($file_type, $access_typies)) { $file_type = "IMAGE"; } if($file_type == "FLASH") { $flashEnabled = true; static $flashMime = array("application/x-shockwave-flash", "application/vnd.adobe.flash.movie"); $res = static::CheckFile($arFile, $iMaxSize, $flashMime, static::GetFlashExtensions(), $bForceMD5, $bSkipExt); } else { $res = static::CheckFile($arFile, $iMaxSize, "image/", static::GetImageExtensions(), $bForceMD5, $bSkipExt); } if($res <> '') { return $res; } $imgInfo = (new File\Image($arFile["tmp_name"]))->getInfo($flashEnabled); if($imgInfo) { $intWIDTH = $imgInfo->getWidth(); $intHEIGHT = $imgInfo->getHeight(); } else { return GetMessage("FILE_BAD_FILE_TYPE").".<br>"; } //check for dimensions if($iMaxWidth > 0 && ($intWIDTH > $iMaxWidth || $intWIDTH == 0) || $iMaxHeight > 0 && ($intHEIGHT > $iMaxHeight || $intHEIGHT == 0)) { return GetMessage("FILE_BAD_MAX_RESOLUTION")." (".$iMaxWidth." * ".$iMaxHeight." ".GetMessage("main_include_dots").").<br>"; } return null; } public static function CheckFile($arFile, $intMaxSize=0, $mimeType=false, $strExt=false, $bForceMD5=false, $bSkipExt=false) { if($arFile["name"] == "") { return ""; } //translit, replace unsafe chars, etc. $strFileName = self::transformName($arFile["name"], $bForceMD5, $bSkipExt); //transformed name must be valid, check disk quota, etc. if(($error = self::validateFile($strFileName, $arFile)) <> '') { return $error; } if($intMaxSize > 0 && $arFile["size"] > $intMaxSize) { return GetMessage("FILE_BAD_SIZE")." (".static::FormatSize($intMaxSize).")."; } $strFileExt = ''; if($strExt) { $strFileExt = GetFileExtension($strFileName); if($strFileExt == '') { return GetMessage("FILE_BAD_TYPE"); } } //Check mime type if($mimeType !== false) { if(!is_array($mimeType)) { $mimeType = array($mimeType); } $goodMime = false; foreach($mimeType as $strMimeType) { if(substr($arFile["type"], 0, strlen($strMimeType)) == $strMimeType) { $goodMime = true; break; } } if(!$goodMime) { return GetMessage("FILE_BAD_TYPE"); } } //Check extension if($strExt === false) { return ""; } $IsExtCorrect = true; if($strExt) { $IsExtCorrect = false; $tok = strtok($strExt,","); while($tok) { if(strtolower(trim($tok)) == strtolower($strFileExt)) { $IsExtCorrect = true; break; } $tok = strtok(","); } } if($IsExtCorrect) { return ""; } return GetMessage("FILE_BAD_TYPE")." (".strip_tags($strFileExt).")"; } public static function ShowFile($iFileID, $max_file_size=0, $iMaxW=0, $iMaxH=0, $bPopup=false, $sParams=false, $sPopupTitle=false, $iSizeWHTTP=0, $iSizeHHTTP=0) { $strResult = ""; $arFile = static::GetFileArray($iFileID); if($arFile) { $max_file_size = intval($max_file_size); if($max_file_size <= 0) $max_file_size = 1000000000; $ct = $arFile["CONTENT_TYPE"]; if($arFile["FILE_SIZE"] <= $max_file_size && static::IsImage($arFile["SRC"], $ct)) { $strResult = static::ShowImage($arFile, $iMaxW, $iMaxH, $sParams, "", $bPopup, $sPopupTitle, $iSizeWHTTP, $iSizeHHTTP); } else { $strResult = '<a href="'.htmlspecialcharsbx($arFile["SRC"]).'" title="'.GetMessage("FILE_FILE_DOWNLOAD").'">'.htmlspecialcharsbx($arFile["FILE_NAME"]).'</a>'; } } return $strResult; } public static function DisableJSFunction($b=true) { global $SHOWIMAGEFIRST; $SHOWIMAGEFIRST = $b; } public static function OutputJSImgShw() { global $SHOWIMAGEFIRST; if(!defined("ADMIN_SECTION") && $SHOWIMAGEFIRST!==true) { echo '<script type="text/javascript"> function ImgShw(ID, width, height, alt) { var scroll = "no"; var top=0, left=0; var w, h; if(navigator.userAgent.toLowerCase().indexOf("opera") != -1) { w = document.body.offsetWidth; h = document.body.offsetHeight; } else { w = screen.width; h = screen.height; } if(width > w-10 || height > h-28) scroll = "yes"; if(height < h-28) top = Math.floor((h - height)/2-14); if(width < w-10) left = Math.floor((w - width)/2-5); width = Math.min(width, w-10); height = Math.min(height, h-28); var wnd = window.open("","","scrollbars="+scroll+",resizable=yes,width="+width+",height="+height+",left="+left+",top="+top); wnd.document.write( "<html><head>"+ "<"+"script type=\"text/javascript\">"+ "function KeyPress(e)"+ "{"+ " if (!e) e = window.event;"+ " if(e.keyCode == 27) "+ " window.close();"+ "}"+ "</"+"script>"+ "<title>"+(alt == ""? "'.GetMessage("main_js_img_title").'":alt)+"</title></head>"+ "<body topmargin=\"0\" leftmargin=\"0\" marginwidth=\"0\" marginheight=\"0\" onKeyDown=\"KeyPress(arguments[0])\">"+ "<img src=\""+ID+"\" border=\"0\" alt=\""+alt+"\" />"+ "</body></html>" ); wnd.document.close(); wnd.focus(); } </script>'; $SHOWIMAGEFIRST=true; } } public static function _GetImgParams($strImage, $iSizeWHTTP=0, $iSizeHHTTP=0) { global $arCloudImageSizeCache; $io = CBXVirtualIo::GetInstance(); if($strImage == '') return false; $strAlt = ''; if(intval($strImage)>0) { $db_img_arr = static::GetFileArray($strImage); if($db_img_arr) { $strImage = $db_img_arr["SRC"]; $intWidth = intval($db_img_arr["WIDTH"]); $intHeight = intval($db_img_arr["HEIGHT"]); $strAlt = $db_img_arr["DESCRIPTION"]; } else { return false; } } else { if(!preg_match("#^https?://#", $strImage)) { $imageInfo = (new File\Image($io->GetPhysicalName($_SERVER["DOCUMENT_ROOT"].$strImage)))->getInfo(); if($imageInfo) { $intWidth = $imageInfo->getWidth(); $intHeight = $imageInfo->getHeight(); } else { return false; } } elseif(array_key_exists($strImage, $arCloudImageSizeCache)) { $intWidth = $arCloudImageSizeCache[$strImage][0]; $intHeight = $arCloudImageSizeCache[$strImage][1]; } else { $intWidth = intval($iSizeWHTTP); $intHeight = intval($iSizeHHTTP); } } return array( "SRC"=>$strImage, "WIDTH"=>$intWidth, "HEIGHT"=>$intHeight, "ALT"=>$strAlt, ); } /** * Retuns the path from the root by a file ID. * * @param int $img_id File ID * @return string|null */ public static function GetPath($img_id) { $img_id = intval($img_id); if($img_id > 0) { $res = static::_GetImgParams($img_id); return is_array($res) ? $res["SRC"] : null; } return null; } public static function ShowImage($strImage, $iMaxW=0, $iMaxH=0, $sParams=null, $strImageUrl="", $bPopup=false, $sPopupTitle=false, $iSizeWHTTP=0, $iSizeHHTTP=0, $strImageUrlTemplate="") { if(is_array($strImage)) { $arImgParams = $strImage; $iImageID = isset($arImgParams['ID']) ? (int)$arImgParams['ID'] : 0; } else { $arImgParams = static::_GetImgParams($strImage, $iSizeWHTTP, $iSizeHHTTP); $iImageID = (int)$strImage; } if(!$arImgParams) { return ""; } $iMaxW = (int)$iMaxW; $iMaxH = (int)$iMaxH; $intWidth = (int)$arImgParams['WIDTH']; $intHeight = (int)$arImgParams['HEIGHT']; if( $iMaxW > 0 && $iMaxH > 0 && ($intWidth > $iMaxW || $intHeight > $iMaxH) ) { $coeff = ($intWidth/$iMaxW > $intHeight/$iMaxH? $intWidth/$iMaxW : $intHeight/$iMaxH); $iHeight = (int)roundEx($intHeight/$coeff); $iWidth = (int)roundEx($intWidth/$coeff); } else { $coeff = 1; $iHeight = $intHeight; $iWidth = $intWidth; } $strImageUrlTemplate = strval($strImageUrlTemplate); if($strImageUrlTemplate === '' || $iImageID <= 0) { $strImage = $arImgParams['SRC']; } else { $strImage = CComponentEngine::MakePathFromTemplate($strImageUrlTemplate, array('file_id' => $iImageID)); } $strImage = Uri::urnEncode($strImage); if(GetFileType($strImage) == "FLASH") { $strReturn = ' <object classid="clsid:D27CDB6E-AE6D-11CF-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" id="banner" WIDTH="'.$iWidth.'" HEIGHT="'.$iHeight.'" ALIGN=""> <PARAM NAME="movie" VALUE="'.htmlspecialcharsbx($strImage).'" /> <PARAM NAME="quality" VALUE="high" /> <PARAM NAME="bgcolor" VALUE="#FFFFFF" /> <embed src="'.htmlspecialcharsbx($strImage).'" quality="high" bgcolor="#FFFFFF" WIDTH="'.$iWidth.'" HEIGHT="'.$iHeight.'" NAME="banner" ALIGN="" TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer"> </embed> </object> '; } else { $strAlt = $arImgParams['ALT'] ?? ($arImgParams['DESCRIPTION'] ?? ''); if($sParams === null || $sParams === false) { $sParams = 'border="0" alt="'.htmlspecialcharsEx($strAlt).'"'; } elseif(!preg_match('/(^|\\s)alt\\s*=\\s*(["\']?)(.*?)(\\2)/is', $sParams)) { $sParams .= ' alt="'.htmlspecialcharsEx($strAlt).'"'; } if($coeff === 1 || !$bPopup) { $strReturn = '<img src="'.htmlspecialcharsbx($strImage).'" '.$sParams.' width="'.$iWidth.'" height="'.$iHeight.'" />'; } else { if($sPopupTitle === false) { $sPopupTitle = GetMessage('FILE_ENLARGE'); } if($strImageUrl <> '') { $strReturn = '<a href="'.$strImageUrl.'" title="'.htmlspecialcharsEx($sPopupTitle).'" target="_blank">'. '<img src="'.htmlspecialcharsbx($strImage).'" '.$sParams.' width="'.$iWidth.'" height="'.$iHeight.'" title="'.htmlspecialcharsEx($sPopupTitle).'" />'. '</a>'; } else { static::OutputJSImgShw(); $strReturn = '<a title="'.$sPopupTitle.'" '. 'onclick="ImgShw(\''.htmlspecialcharsbx(CUtil::addslashes($strImage)).'\', '.$intWidth.', '.$intHeight.', \''.CUtil::addslashes(htmlspecialcharsEx(htmlspecialcharsEx($strAlt))).'\'); return false;" '. 'href="'.htmlspecialcharsbx($strImage).'" '. 'target="_blank"'. '>'. '<img src="'.htmlspecialcharsbx($strImage).'" '.$sParams.' width="'.$iWidth.'" height="'.$iHeight.'" />'. '</a>'; } } } return $bPopup? $strReturn : print_url($strImageUrl, $strReturn); } public static function Show2Images($strImage1, $strImage2, $iMaxW=0, $iMaxH=0, $sParams=false, $sPopupTitle=false, $iSizeWHTTP=0, $iSizeHHTTP=0) { if(!($arImgParams = static::_GetImgParams($strImage1, $iSizeWHTTP, $iSizeHHTTP))) return ""; $strImage1 = Uri::urnEncode($arImgParams["SRC"], "UTF-8"); $intWidth = $arImgParams["WIDTH"]; $intHeight = $arImgParams["HEIGHT"]; $strAlt = $arImgParams["ALT"]; if($sParams == false) $sParams = 'border="0" alt="'.htmlspecialcharsEx($strAlt).'"'; elseif(!preg_match("/(^|\\s)alt\\s*=\\s*([\"']?)(.*?)(\\2)/is", $sParams)) $sParams .= ' alt="'.htmlspecialcharsEx($strAlt).'"'; if( $iMaxW > 0 && $iMaxH > 0 && ($intWidth > $iMaxW || $intHeight > $iMaxH) ) { $coeff = ($intWidth/$iMaxW > $intHeight/$iMaxH? $intWidth/$iMaxW : $intHeight/$iMaxH); $iHeight = intval(roundEx($intHeight/$coeff)); $iWidth = intval(roundEx($intWidth/$coeff)); } else { $iHeight = $intHeight; $iWidth = $intWidth; } if($arImgParams = static::_GetImgParams($strImage2, $iSizeWHTTP, $iSizeHHTTP)) { if($sPopupTitle === false) $sPopupTitle = GetMessage("FILE_ENLARGE"); $strImage2 = Uri::urnEncode($arImgParams["SRC"], "UTF-8"); $intWidth2 = $arImgParams["WIDTH"]; $intHeight2 = $arImgParams["HEIGHT"]; $strAlt2 = $arImgParams["ALT"]; static::OutputJSImgShw(); $strReturn = "<a title=\"".$sPopupTitle."\" onclick=\"ImgShw('".CUtil::addslashes($strImage2)."','".$intWidth2."','".$intHeight2."', '".CUtil::addslashes(htmlspecialcharsEx(htmlspecialcharsEx($strAlt2)))."'); return false;\" href=\"".$strImage2."\" target=_blank>". "<img src=\"".$strImage1."\" ".$sParams." width=".$iWidth." height=".$iHeight." /></a>"; } else { $strReturn = "<img src=\"".$strImage1."\" ".$sParams." width=".$iWidth." height=".$iHeight." />"; } return $strReturn; } /** * Returns an array describing file as if it was $_FILES element. * * @param string|int $path May contain ID of the file, absolute path, relative path or an url. * @param string|bool $mimetype Forces type field of the array * @param bool $skipInternal Excludes using ID as $path * @param string $external_id * @return array|bool|null */ public static function MakeFileArray($path, $mimetype = false, $skipInternal = false, $external_id = "") { $io = CBXVirtualIo::GetInstance(); $arFile = array(); if(intval($path)>0) { if ($skipInternal) return false; $res = static::GetByID($path); if($ar = $res->Fetch()) { $bExternalStorage = false; foreach(GetModuleEvents("main", "OnMakeFileArray", true) as $arEvent) { if(ExecuteModuleEventEx($arEvent, array($ar, &$arFile))) { $bExternalStorage = true; break; } } if(!$bExternalStorage) { $arFile["name"] = ($ar['ORIGINAL_NAME'] <> ''?$ar['ORIGINAL_NAME']:$ar['FILE_NAME']); $arFile["size"] = $ar['FILE_SIZE']; $arFile["type"] = $ar['CONTENT_TYPE']; $arFile["description"] = $ar['DESCRIPTION']; $arFile["tmp_name"] = $io->GetPhysicalName(preg_replace("#[\\\\\\/]+#", "/", $_SERVER['DOCUMENT_ROOT'].'/'.(COption::GetOptionString('main', 'upload_dir', 'upload')).'/'.$ar['SUBDIR'].'/'.$ar['FILE_NAME'])); } if (!isset($arFile["external_id"])) { $arFile["external_id"] = $external_id != ""? $external_id: $ar["EXTERNAL_ID"]; } return $arFile; } } $path = preg_replace("#(?<!:)[\\\\\\/]+#", "/", $path); if($path == '' || $path == "/") { return null; } if (preg_match("#^(php://|phar://)#i", $path) && !preg_match("#^php://input$#i", $path)) { return null; } if (preg_match("#^https?://#i", $path)) { $temp_path = ''; $bExternalStorage = false; foreach(GetModuleEvents("main", "OnMakeFileArray", true) as $arEvent) { if(ExecuteModuleEventEx($arEvent, array($path, &$temp_path))) { $bExternalStorage = true; break; } } if(!$bExternalStorage) { $http = new Web\HttpClient(); $http->setPrivateIp(false); $temp_path = static::GetTempName('', 'tmp.'.md5(mt_rand())); if($http->download($path, $temp_path)) { $arFile = static::MakeFileArray($temp_path); if($arFile) { $urlComponents = parse_url($path); if($urlComponents && $urlComponents["path"] <> '') { $arFile["name"] = $io->GetLogicalName(bx_basename($urlComponents["path"])); } else { $arFile["name"] = $io->GetLogicalName(bx_basename($path)); } } } } elseif($temp_path) { $arFile = static::MakeFileArray($temp_path); } } elseif(preg_match("#^(ftp[s]?://|php://input)#i", $path)) { if($fp = fopen($path,"rb")) { $content = ""; while(!feof($fp)) { $content .= fgets($fp, 4096); } if($content <> '') { $temp_path = static::GetTempName('', 'tmp.'.md5(mt_rand())); if(RewriteFile($temp_path, $content)) { $arFile = static::MakeFileArray($temp_path); if($arFile) { $arFile["name"] = $io->GetLogicalName(bx_basename($path)); } } } fclose($fp); } } else { if(!file_exists($path)) { if (file_exists($_SERVER["DOCUMENT_ROOT"].$path)) $path = $_SERVER["DOCUMENT_ROOT"].$path; else return null; } if(is_dir($path)) return null; $arFile["name"] = $io->GetLogicalName(bx_basename($path)); $arFile["size"] = filesize($path); $arFile["tmp_name"] = $path; $arFile["type"] = $mimetype; if($arFile["type"] == '') $arFile["type"] = static::GetContentType($path, true); } if($arFile["type"] == '') $arFile["type"] = "unknown"; if (!isset($arFile["external_id"]) && ($external_id != "")) { $arFile["external_id"] = $external_id; } return $arFile; } public static function GetTempName($dir_name = false, $file_name = '') { //accidentally $file_name can contain "?params" if(($pos = mb_strpos($file_name, "?")) !== false) { $file_name = mb_substr($file_name, 0, $pos); } return CTempFile::GetFileName($file_name); } public static function ChangeSubDir($module_id, $old_subdir, $new_subdir) { global $DB; if ($old_subdir!=$new_subdir) { $strSql = " UPDATE b_file SET SUBDIR = REPLACE(SUBDIR,'".$DB->ForSQL($old_subdir)."','".$DB->ForSQL($new_subdir)."'), TIMESTAMP_X = ".$DB->GetNowFunction()." WHERE MODULE_ID='".$DB->ForSQL($module_id)."' "; if($rs = $DB->Query($strSql)) { $from = "/".COption::GetOptionString("main", "upload_dir", "upload")."/".$old_subdir; $to = "/".COption::GetOptionString("main", "upload_dir", "upload")."/".$new_subdir; CopyDirFiles($_SERVER["DOCUMENT_ROOT"].$from, $_SERVER["DOCUMENT_ROOT"].$to, true, true, true); //Reset All b_file cache $cache = Main\Application::getInstance()->getManagedCache(); $cache->cleanDir(self::CACHE_DIR); } } } public static function ResizeImage(&$arFile, $arSize, $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL) { $io = CBXVirtualIo::GetInstance(); // $arFile["tmp_name"] should contain physical filename $destinationFile = CTempFile::GetFileName(basename($arFile["tmp_name"])); $sourceFile = $io->GetLogicalName($arFile["tmp_name"]); CheckDirPath($destinationFile); if (static::ResizeImageFile($sourceFile, $destinationFile, $arSize, $resizeType)) { $arFile["tmp_name"] = $io->GetPhysicalName($destinationFile); $imageInfo = (new File\Image($arFile["tmp_name"]))->getInfo(); if ($imageInfo) { $arFile["type"] = $imageInfo->getMime(); } $arFile["size"] = filesize($arFile["tmp_name"]); return true; } return false; } public static function ResizeImageDeleteCache($arFile) { $temp_dir = CTempFile::GetAbsoluteRoot()."/"; if(mb_strpos($arFile["tmp_name"], $temp_dir) === 0) if(file_exists($arFile["tmp_name"])) unlink($arFile["tmp_name"]); } public static function ResizeImageGet($file, $arSize, $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL, $bInitSizes = false, $arFilters = false, $bImmediate = false, $jpgQuality = false) { if (!is_array($file) && intval($file) > 0) { $file = static::GetFileArray($file); } if (!is_array($file) || !array_key_exists("FILE_NAME", $file) || $file["FILE_NAME"] == '') return false; if ($resizeType !== BX_RESIZE_IMAGE_EXACT && $resizeType !== BX_RESIZE_IMAGE_PROPORTIONAL_ALT) $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL; if (!is_array($arSize)) $arSize = array(); if (!array_key_exists("width", $arSize) || intval($arSize["width"]) <= 0) $arSize["width"] = 0; if (!array_key_exists("height", $arSize) || intval($arSize["height"]) <= 0) $arSize["height"] = 0; $arSize["width"] = intval($arSize["width"]); $arSize["height"] = intval($arSize["height"]); $uploadDirName = COption::GetOptionString("main", "upload_dir", "upload"); $imageFile = "/".$uploadDirName."/".$file["SUBDIR"]."/".$file["FILE_NAME"]; $arImageSize = false; $bFilters = is_array($arFilters) && !empty($arFilters); if ( ($arSize["width"] <= 0 || $arSize["width"] >= $file["WIDTH"]) && ($arSize["height"] <= 0 || $arSize["height"] >= $file["HEIGHT"]) ) { if($bFilters) { //Only filters. Leave size unchanged $arSize["width"] = $file["WIDTH"]; $arSize["height"] = $file["HEIGHT"]; $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL; } else { if (isset($file["SRC"])) { global $arCloudImageSizeCache; $arCloudImageSizeCache[$file["SRC"]] = array($file["WIDTH"], $file["HEIGHT"]); } else { trigger_error("Parameter \$file for CFile::ResizeImageGet does not have SRC element. You'd better pass an b_file.ID as a value for the \$file parameter.", E_USER_WARNING); } return array( "src" => $file["SRC"], "width" => intval($file["WIDTH"]), "height" => intval($file["HEIGHT"]), "size" => $file["FILE_SIZE"], ); } } $io = CBXVirtualIo::GetInstance(); $cacheImageFile = "/".$uploadDirName."/resize_cache/".$file["SUBDIR"]."/".$arSize["width"]."_".$arSize["height"]."_".$resizeType.(is_array($arFilters)? md5(serialize($arFilters)): "")."/".$file["FILE_NAME"]; $cacheImageFileCheck = $cacheImageFile; if ($file["CONTENT_TYPE"] == "image/bmp") $cacheImageFileCheck .= ".jpg"; static $cache = array(); $cache_id = $cacheImageFileCheck; if(isset($cache[$cache_id])) { return $cache[$cache_id]; } elseif (!file_exists($io->GetPhysicalName($_SERVER["DOCUMENT_ROOT"].$cacheImageFileCheck))) { if(!is_array($arFilters)) $arFilters = array( array("name" => "sharpen", "precision" => 15), ); $sourceImageFile = $_SERVER["DOCUMENT_ROOT"].$imageFile; $cacheImageFileTmp = $_SERVER["DOCUMENT_ROOT"].$cacheImageFile; $bNeedResize = true; $callbackData = null; foreach(GetModuleEvents("main", "OnBeforeResizeImage", true) as $arEvent) { if(ExecuteModuleEventEx($arEvent, array( $file, array($arSize, $resizeType, array(), false, $arFilters, $bImmediate), &$callbackData, &$bNeedResize, &$sourceImageFile, &$cacheImageFileTmp, ))) break; } if ($bNeedResize && static::ResizeImageFile($sourceImageFile, $cacheImageFileTmp, $arSize, $resizeType, array(), $jpgQuality, $arFilters)) { $cacheImageFile = mb_substr($cacheImageFileTmp, mb_strlen($_SERVER["DOCUMENT_ROOT"])); /****************************** QUOTA ******************************/ if (COption::GetOptionInt("main", "disk_space") > 0) CDiskQuota::updateDiskQuota("file", filesize($io->GetPhysicalName($cacheImageFileTmp)), "insert"); /****************************** QUOTA ******************************/ } else { $cacheImageFile = $imageFile; } foreach(GetModuleEvents("main", "OnAfterResizeImage", true) as $arEvent) { if(ExecuteModuleEventEx($arEvent, array( $file, array($arSize, $resizeType, array(), false, $arFilters), &$callbackData, &$cacheImageFile, &$cacheImageFileTmp, &$arImageSize, ))) break; } $cacheImageFileCheck = $cacheImageFile; } elseif (defined("BX_FILE_USE_FLOCK")) { $hLock = $io->OpenFile($_SERVER["DOCUMENT_ROOT"].$imageFile, "r+"); if ($hLock) { flock($hLock, LOCK_EX); flock($hLock, LOCK_UN); fclose($hLock); } } if ($bInitSizes && !is_array($arImageSize)) { $imageInfo = (new File\Image($_SERVER["DOCUMENT_ROOT"].$cacheImageFileCheck))->getInfo(); if($imageInfo) { $arImageSize[0] = $imageInfo->getWidth(); $arImageSize[1] = $imageInfo->getHeight(); } else { $arImageSize = [0, 0]; } $f = $io->GetFile($_SERVER["DOCUMENT_ROOT"].$cacheImageFileCheck); $arImageSize[2] = $f->GetFileSize(); } if (!is_array($arImageSize)) { $arImageSize = [0, 0, 0]; } $cache[$cache_id] = array( "src" => $cacheImageFileCheck, "width" => intval($arImageSize[0]), "height" => intval($arImageSize[1]), "size" => $arImageSize[2], ); return $cache[$cache_id]; } public static function ResizeImageDelete($arImage) { $io = CBXVirtualIo::GetInstance(); $upload_dir = COption::GetOptionString("main", "upload_dir", "upload"); $disk_space = COption::GetOptionInt("main", "disk_space"); $delete_size = 0; $d = $io->GetDirectory($_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/resize_cache/".$arImage["SUBDIR"]); /** @var CBXVirtualFileFileSystem|CBXVirtualDirectoryFileSystem $dir_entry */ foreach($d->GetChildren() as $dir_entry) { if($dir_entry->IsDirectory()) { $f = $io->GetFile($dir_entry->GetPathWithName()."/".$arImage["FILE_NAME"]); if($f->IsExists()) { if ($disk_space > 0) { $fileSizeTmp = $f->GetFileSize(); if ($io->Delete($f->GetPathWithName())) $delete_size += $fileSizeTmp; } else { $io->Delete($f->GetPathWithName()); } } try { @rmdir($io->GetPhysicalName($dir_entry->GetPathWithName())); } catch(\ErrorException $exception) { // Ignore a E_WARNING Error } } } try { @rmdir($io->GetPhysicalName($d->GetPathWithName())); } catch(\ErrorException $exception) { // Ignore a E_WARNING Error } return $delete_size; } /** * @deprecated Use imagecreatefrombmp() * @param $filename * @return false|resource */ public static function ImageCreateFromBMP($filename) { return imagecreatefrombmp($filename); } /** * @deprecated Use \Bitrix\Main\File\Image\Rectangle::resize() * @param $sourceImageWidth * @param $sourceImageHeight * @param $arSize * @param $resizeType * @param $bNeedCreatePicture * @param $arSourceSize * @param $arDestinationSize */ public static function ScaleImage($sourceImageWidth, $sourceImageHeight, $arSize, $resizeType, &$bNeedCreatePicture, &$arSourceSize, &$arDestinationSize) { $source = new Rectangle($sourceImageWidth, $sourceImageHeight); $destination = new Rectangle($arSize["width"], $arSize["height"]); $bNeedCreatePicture = $source->resize($destination, $resizeType); $arSourceSize = [ "x" => $source->getX(), "y" => $source->getY(), "width" => $source->getWidth(), "height" => $source->getHeight(), ]; $arDestinationSize = [ "x" => $destination->getX(), "y" => $destination->getY(), "width" => $destination->getWidth(), "height" => $destination->getHeight(), ]; } /** * @deprecated Always returns true. * @return bool */ public static function IsGD2() { return true; } public static function ResizeImageFile($sourceFile, &$destinationFile, $arSize, $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL, $arWaterMark = array(), $quality=false, $arFilters=false) { $io = CBXVirtualIo::GetInstance(); if (!$io->FileExists($sourceFile)) return false; if ($resizeType !== BX_RESIZE_IMAGE_EXACT && $resizeType !== BX_RESIZE_IMAGE_PROPORTIONAL_ALT) $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL; if (!is_array($arSize)) $arSize = array(); if (!array_key_exists("width", $arSize) || intval($arSize["width"]) <= 0) $arSize["width"] = 0; if (!array_key_exists("height", $arSize) || intval($arSize["height"]) <= 0) $arSize["height"] = 0; $arSize["width"] = intval($arSize["width"]); $arSize["height"] = intval($arSize["height"]); $sourceImage = new File\Image($io->GetPhysicalName($sourceFile)); $sourceInfo = $sourceImage->getInfo(); if ($sourceInfo === null || !$sourceInfo->isSupported()) { return false; } $fileType = $sourceInfo->getFormat(); $orientation = 0; if($fileType == File\Image::FORMAT_JPEG) { $exifData = $sourceImage->getExifData(); if (isset($exifData['Orientation'])) { $orientation = $exifData['Orientation']; //swap width and height if ($orientation >= 5 && $orientation <= 8) { $sourceInfo->swapSides(); } } } $result = false; $sourceRectangle = $sourceInfo->toRectangle(); $destinationRectangle = new Rectangle($arSize["width"], $arSize["height"]); $needResize = $sourceRectangle->resize($destinationRectangle, $resizeType); $hLock = $io->OpenFile($sourceFile, "r+"); $useLock = defined("BX_FILE_USE_FLOCK"); if ($hLock) { if ($useLock) { flock($hLock, LOCK_EX); } if ($io->FileExists($destinationFile)) { $destinationInfo = (new File\Image($io->GetPhysicalName($destinationFile)))->getInfo(); if ($destinationInfo) { if($destinationInfo->getWidth() == $destinationRectangle->getWidth() && $destinationInfo->getHeight() == $destinationRectangle->getHeight()) { //nothing to do $result = true; } } } } if($result === false) { if ($io->Copy($sourceFile, $destinationFile)) { $destinationImage = new File\Image($io->GetPhysicalName($destinationFile)); if($destinationImage->load()) { if ($orientation > 1) { $destinationImage->autoRotate($orientation); } $modified = false; if ($needResize) { // actual sizes $sourceRectangle = $destinationImage->getDimensions(); $destinationRectangle = new Rectangle($arSize["width"], $arSize["height"]); $sourceRectangle->resize($destinationRectangle, $resizeType); $modified = $destinationImage->resize($sourceRectangle, $destinationRectangle); } if(!is_array($arFilters)) { $arFilters = []; } if(is_array($arWaterMark)) { $arWaterMark["name"] = "watermark"; $arFilters[] = $arWaterMark; } foreach($arFilters as $arFilter) { if($arFilter["name"] == "sharpen" && $arFilter["precision"] > 0) { $modified |= $destinationImage->filter(File\Image\Mask::createSharpen($arFilter["precision"])); } elseif($arFilter["name"] == "watermark") { $watermark = Image\Watermark::createFromArray($arFilter); $modified |= $destinationImage->drawWatermark($watermark); } } if($modified) { if($quality === false) { $quality = COption::GetOptionString('main', 'image_resize_quality'); } $io->Delete($destinationFile); if($fileType == File\Image::FORMAT_BMP) { $destinationFile .= ".jpg"; $destinationImage->saveAs($io->GetPhysicalName($destinationFile), $quality, File\Image::FORMAT_JPEG); } else { $destinationImage->save($quality); } $destinationImage->clear(); } } $result = true; } } if ($hLock) { if ($useLock) { flock($hLock, LOCK_UN); } fclose($hLock); } return $result; } /** * @deprecated Use \Bitrix\Main\File\Image * @param $picture * @param $arFilter * @return bool */ public static function ApplyImageFilter($picture, $arFilter) { //prevents destroing outside the function static $engine; switch($arFilter["name"]) { case "sharpen": $precision = intval($arFilter["precision"]); if($precision > 0) { $engine = new File\Image\Gd(); $engine->setResource($picture); return $engine->filter(File\Image\Mask::createSharpen($precision)); } return false; case "watermark": return static::WaterMark($picture, $arFilter); } return false; } /** * @deprecated Use \Bitrix\Main\File\Image * @param $picture * @param $matrix */ public static function imageconvolution($picture, $matrix) { } /** * @deprecated Use \Bitrix\Main\File\Image * @param $picture * @param $matrix * @param int $div * @param int $offset */ public static function imageconvolution_fix($picture, $matrix, $div = 1, $offset = 0) { } /** * @deprecated Use \Bitrix\Main\File\Image * @param $picture */ public static function ImageFlipHorizontal($picture) { //prevents destroing outside the function static $engine; $engine = new File\Image\Gd(); $engine->setResource($picture); $engine->flipHorizontal(); } /** * @deprecated Use \Bitrix\Main\File\Image::autoRotate() * @param $orientation * @param $sourceImage * @return false|resource */ public static function ImageHandleOrientation($orientation, $sourceImage) { if ($orientation <= 1) { return false; } if (!is_resource($sourceImage)) { //file $image = new File\Image($sourceImage); if($image->load()) { if($image->autoRotate($orientation)) { $quality = COption::GetOptionString('main', 'image_resize_quality'); $image->save($quality); } } return false; } //prevents destroing outside the function static $engine; //compatibility around GD image resource $engine = new File\Image\Gd(); $engine->setResource($sourceImage); $image = new File\Image(); $image->setEngine($engine); $image->autoRotate($orientation); return $engine->getResource(); } /** * @param int|array $arFile * @param array $arOptions * @return bool */ public static function ViewByUser($arFile, $arOptions = array()) { $previewManager = new Viewer\PreviewManager(); if ($previewManager->isInternalRequest($arFile, $arOptions)) { $previewManager->processViewByUserRequest($arFile, $arOptions); } /** @global CMain $APPLICATION */ global $APPLICATION; $fastDownload = (COption::GetOptionString('main', 'bx_fast_download', 'N') == 'Y'); $attachment_name = ""; $content_type = ""; $specialchars = false; $force_download = false; $cache_time = 10800; $fromClouds = false; $filename = ''; $fromTemp = false; if(is_array($arOptions)) { if(isset($arOptions["content_type"])) $content_type = $arOptions["content_type"]; if(isset($arOptions["specialchars"])) $specialchars = $arOptions["specialchars"]; if(isset($arOptions["force_download"])) $force_download = $arOptions["force_download"]; if(isset($arOptions["cache_time"])) $cache_time = intval($arOptions["cache_time"]); if(isset($arOptions["attachment_name"])) $attachment_name = $arOptions["attachment_name"]; if(isset($arOptions["fast_download"])) $fastDownload = (bool)$arOptions["fast_download"]; } if($cache_time < 0) $cache_time = 0; if(is_array($arFile)) { if(isset($arFile["SRC"])) { $filename = $arFile["SRC"]; } elseif(isset($arFile["tmp_name"])) { if (mb_strpos($arFile['tmp_name'], $_SERVER['DOCUMENT_ROOT']) === 0) { $filename = '/'. ltrim(mb_substr($arFile['tmp_name'], mb_strlen($_SERVER['DOCUMENT_ROOT'])), '/'); } elseif (defined('BX_TEMPORARY_FILES_DIRECTORY') && mb_strpos($arFile['tmp_name'], BX_TEMPORARY_FILES_DIRECTORY) === 0) { $fromTemp = true; $tmpPath = COption::GetOptionString('main', 'bx_tmp_download', '/bx_tmp_download/'); $filename = $tmpPath.ltrim(mb_substr($arFile['tmp_name'], mb_strlen(BX_TEMPORARY_FILES_DIRECTORY)), '/'); //nonexistent path } } else { $filename = static::GetFileSRC($arFile); } } elseif (($arFile = static::GetFileArray($arFile))) { $filename = $arFile['SRC']; } if ($filename == '') { return false; } if($content_type == '' && isset($arFile["CONTENT_TYPE"])) { $content_type = $arFile["CONTENT_TYPE"]; } //we produce resized jpg for original bmp if($content_type == '' || $content_type == "image/bmp") { if(isset($arFile["tmp_name"])) { $content_type = static::GetContentType($arFile["tmp_name"], true); } else { $content_type = static::GetContentType($_SERVER["DOCUMENT_ROOT"].$filename); } } if(isset($arFile["ORIGINAL_NAME"]) && $arFile["ORIGINAL_NAME"] != '') $name = $arFile["ORIGINAL_NAME"]; elseif($arFile["name"] <> '') $name = $arFile["name"]; else $name = $arFile["FILE_NAME"]; if(isset($arFile["EXTENSION_SUFFIX"]) && $arFile["EXTENSION_SUFFIX"] <> '') $name = mb_substr($name, 0, -mb_strlen($arFile["EXTENSION_SUFFIX"])); $name = str_replace(array("\n", "\r"), '', $name); if($attachment_name) $attachment_name = str_replace(array("\n", "\r"), '', $attachment_name); else $attachment_name = $name; if(!$force_download) { if(!static::IsImage($name, $content_type) || $arFile["HEIGHT"] <= 0 || $arFile["WIDTH"] <= 0) { //only valid images can be downloaded inline $force_download = true; } } $content_type = Web\MimeType::normalize($content_type); if($force_download) { $specialchars = false; } $src = null; $file = null; if ((mb_substr($filename, 0, 1) == '/') && !$fromTemp) { $file = new IO\File($_SERVER['DOCUMENT_ROOT']. $filename); } elseif (isset($arFile['tmp_name'])) { $file = new IO\File($arFile['tmp_name']); } if ((mb_substr($filename, 0, 1) == '/') && ($file instanceof IO\File)) { try { $src = $file->open(IO\FileStreamOpenMode::READ); } catch(IO\IoException $e) { return false; } } else { if(!$fastDownload) { $src = new Web\HttpClient(); } elseif(intval($arFile['HANDLER_ID']) > 0) { $fromClouds = true; } } $APPLICATION->RestartBuffer(); $cur_pos = 0; $filesize = (isset($arFile["FILE_SIZE"]) && (int)$arFile["FILE_SIZE"] > 0 ? (int)$arFile["FILE_SIZE"] : (int)($arFile["size"] ?? 0)); $size = $filesize-1; $p = mb_strpos($_SERVER["HTTP_RANGE"] ?? '', "="); if(intval($p)>0) { $bytes = mb_substr($_SERVER["HTTP_RANGE"], $p + 1); $p = mb_strpos($bytes, "-"); if($p !== false) { $cur_pos = floatval(mb_substr($bytes, 0, $p)); $size = floatval(mb_substr($bytes, $p + 1)); if ($size <= 0) { $size = $filesize - 1; } if ($cur_pos > $size) { $cur_pos = 0; $size = $filesize - 1; } } } if ($file instanceof IO\File) { $filetime = $file->getModificationTime(); } elseif(isset($arFile["tmp_name"]) && $arFile["tmp_name"] <> '') { $tmpFile = new IO\File($arFile["tmp_name"]); $filetime = $tmpFile->getModificationTime(); } else { $filetime = intval(MakeTimeStamp($arFile["TIMESTAMP_X"])); } $application = \Bitrix\Main\Application::getInstance(); $response = \Bitrix\Main\Context::getCurrent()->getResponse(); if($_SERVER["REQUEST_METHOD"] == "HEAD") { $response->setStatus("200 OK") ->addHeader("Accept-Ranges", "bytes") ->addHeader("Content-Type", $content_type) ->addHeader("Content-Length", ($size-$cur_pos+1)); if($filetime > 0) { $response->addHeader("Last-Modified", date("r", $filetime)); } } else { $lastModified = ''; if($cache_time > 0) { //Handle ETag $ETag = md5($filename.$filesize.$filetime); if(array_key_exists("HTTP_IF_NONE_MATCH", $_SERVER) && ($_SERVER['HTTP_IF_NONE_MATCH'] === $ETag)) { $response->setStatus("304 Not Modified"); $response->addHeader("Cache-Control", "private, max-age=".$cache_time.", pre-check=".$cache_time); $response->writeHeaders(); self::terminate(); } $response->addHeader("ETag", $ETag); //Handle Last Modified if($filetime > 0) { $lastModified = gmdate('D, d M Y H:i:s', $filetime).' GMT'; if(array_key_exists("HTTP_IF_MODIFIED_SINCE", $_SERVER) && ($_SERVER['HTTP_IF_MODIFIED_SINCE'] === $lastModified)) { $response->setStatus("304 Not Modified"); $response->addHeader("Cache-Control", "private, max-age=".$cache_time.", pre-check=".$cache_time); $response->writeHeaders(); self::terminate(); } } } $utfName = Uri::urnEncode($attachment_name, "UTF-8"); $translitName = CUtil::translit($attachment_name, LANGUAGE_ID, array( "max_len" => 1024, "safe_chars" => ".", "replace_space" => '-', "change_case" => false, )); if($force_download) { //Disable zlib for old versions of php <= 5.3.0 //it has broken Content-Length handling if(ini_get('zlib.output_compression')) ini_set('zlib.output_compression', 'Off'); if($cur_pos > 0) { $response->setStatus("206 Partial Content"); } else { $response->SetStatus("200 OK"); } $response->addHeader("Content-Type", $content_type) ->addHeader("Content-Disposition", "attachment; filename=\"".$translitName."\"; filename*=utf-8''".$utfName) ->addHeader("Content-Transfer-Encoding", "binary") ->addHeader("Content-Length", ($size-$cur_pos+1)); if(is_resource($src)) { $response->addHeader("Accept-Ranges", "bytes"); $response->addHeader("Content-Range", "bytes ".$cur_pos."-".$size."/".$filesize); } } else { $response->addHeader("Content-Type", $content_type); $response->addHeader("Content-Disposition", "inline; filename=\"".$translitName."\"; filename*=utf-8''".$utfName); } if($cache_time > 0) { $response->addHeader("Cache-Control", "private, max-age=".$cache_time.", pre-check=".$cache_time); if($filetime > 0) { $response->addHeader('Last-Modified', $lastModified); } } else { $response->addHeader("Cache-Control", "no-cache, must-revalidate, post-check=0, pre-check=0"); } $response->addHeader("Expires", "0"); $response->addHeader("Pragma", "public"); $filenameEncoded = Uri::urnEncode($filename, "UTF-8"); // Download from front-end if($fastDownload) { if($fromClouds) { $filenameDisableProto = preg_replace('~^(https?)(\://)~i', '\\1.' , $filenameEncoded); $cloudUploadPath = COption::GetOptionString('main', 'bx_cloud_upload', '/upload/bx_cloud_upload/'); $response->addHeader('X-Accel-Redirect', rawurlencode($cloudUploadPath.$filenameDisableProto)); } else { $response->addHeader('X-Accel-Redirect', $filenameEncoded); } $response->writeHeaders(); self::terminate(); } else { session_write_close(); $response->writeHeaders(); if ($specialchars) { /** @var IO\File $file */ echo "<", "pre" ,">"; if(is_resource($src)) { while(!feof($src)) echo htmlspecialcharsbx(fread($src, 32768)); $file->close(); } else { /** @var Web\HttpClient $src */ echo htmlspecialcharsbx($src->get($filenameEncoded)); } echo "<", "/pre", ">"; } else { if(is_resource($src)) { /** @var IO\File $file */ $file->seek($cur_pos); while(!feof($src) && ($cur_pos <= $size)) { $bufsize = 131072; //128K if($cur_pos + $bufsize > $size) $bufsize = $size - $cur_pos + 1; $cur_pos += $bufsize; echo fread($src, $bufsize); } $file->close(); } else { $fp = fopen("php://output", "wb"); /** @var Web\HttpClient $src */ $src->setOutputStream($fp); $src->get($filenameEncoded); } } @ob_flush(); flush(); self::terminate(); } } return true; } private static function terminate(): void { /** @see \Bitrix\Main\HttpResponse::flush */ if (function_exists("fastcgi_finish_request")) { //php-fpm fastcgi_finish_request(); } Main\Application::getInstance()->terminate(); } /** * @deprecated Use \Bitrix\Main\File\Image. * @param $obj * @param $Params * type - text|image * size - big|medium|small|real, for custom resizing can be used 'coefficient', real - only for images * position - of the watermark on picture can be in one of two available notifications: * tl|tc|tr|ml|mc|mr|bl|bc|br or topleft|topcenter|topright|centerleft|center|centerright|bottomleft|bottomcenter|bottomright * @return array|bool */ public static function Watermark($obj, $Params) { if ($Params['type'] == 'text') { $result = static::WatermarkText($obj, $Params); } else { $result = static::WatermarkImage($obj, $Params); }; return $result; } /** * @deprecated Use \Bitrix\Main\File\Image::drawWatermark() * @param $obj * @param array $Params * @return bool */ public static function WatermarkText($obj, $Params = array()) { //prevents destroing outside the function static $engine; $engine = new File\Image\Gd(); $engine->setResource($obj); $watermark = Image\Watermark::createFromArray($Params); return $engine->drawTextWatermark($watermark); } /** * Creates watermark from image. * @deprecated Use \Bitrix\Main\File\Image::drawWatermark() * @param $obj * @param array $Params * file - abs path to file * alpha_level - opacity * position - of the watermark * @return bool */ public static function WatermarkImage($obj, $Params = array()) { //prevents destroing outside the function static $engine; $engine = new File\Image\Gd(); $engine->setResource($obj); $watermark = Image\Watermark::createFromArray($Params); return $engine->drawImageWatermark($watermark); } /** * Reads an image from a file, rotates it clockwise, and saves it to the same file. * @param string $sourceFile * @param float $angle * @return bool */ public static function ImageRotate($sourceFile, $angle) { $image = new File\Image($sourceFile); if(!$image->load()) { return false; } $quality = COption::GetOptionString('main', 'image_resize_quality'); $result = ($image->rotate($angle) && $image->save($quality)); $image->clear(); return $result; } /** * @deprecated Use \Bitrix\Main\File\Image * @param string $path * @return false|resource */ public static function CreateImage($path) { $image = new File\Image\Gd($path); if($image->load()) { return $image->getResource(); } return false; } /** * @deprecated Use \Bitrix\Main\File\Image::getExifData() * @param $src * @return array */ public static function ExtractImageExif($src) { return (new File\Image($src))->getExifData(); } /** * @deprecated Use Web\MimeType::normalize() * @param $contentType * @return string */ public static function NormalizeContentType($contentType) { return Web\MimeType::normalize($contentType); } public static function GetContentType($path, $bPhysicalName = false) { if($bPhysicalName) { $pathX = $path; } else { $io = CBXVirtualIo::GetInstance(); $pathX = $io->GetPhysicalName($path); } $type = ""; if (function_exists("mime_content_type")) { $type = mime_content_type($pathX); } if ($type == "" && function_exists("image_type_to_mime_type")) { $info = (new File\Image($pathX))->getInfo(); if($info) { $type = $info->getMime(); } } if ($type == "") { $type = Web\MimeType::getByFileExtension(substr($pathX, bxstrrpos($pathX, ".") + 1)); } return $type; } /** * @deprecated Use \Bitrix\Main\File\Image::getInfo() * @param string $path * @param bool $bPhysicalName * @param bool $flashEnabled * @return array|false */ public static function GetImageSize($path, $bPhysicalName = false, $flashEnabled = false) { if(!$bPhysicalName) { $io = CBXVirtualIo::GetInstance(); $path = $io->GetPhysicalName($path); } $image = new File\Image($path); if(($info = $image->getInfo($flashEnabled)) !== null) { return [ 0 => $info->getWidth(), 1 => $info->getHeight(), 2 => $info->getFormat(), 3 => $info->getAttributes(), "mime" => $info->getMime(), ]; } return false; } /** * @deprecated */ public static function isEnabledTrackingResizeImage() {} /** * @deprecated */ public static function enableTrackingResizeImage() {} /** * @deprecated */ public static function disableTrackingResizeImage() {} public static function DeleteHashAgent() { global $DB; $res = $DB->Query("select distinct h1.FILE_ID FROM b_file_hash h1, b_file_hash h2 WHERE h1.FILE_ID > h2.FILE_ID AND h1.FILE_SIZE = h2.FILE_SIZE AND h1.FILE_HASH = h2.FILE_HASH limit 10000 "); $delete = []; while ($ar = $res->Fetch()) { $delete[] = $ar['FILE_ID']; } if (!empty($delete)) { $DB->Query("DELETE FROM b_file_hash WHERE FILE_ID IN (" . implode(',', $delete) . ")"); return __METHOD__ . '();'; } return ''; } } global $arCloudImageSizeCache; $arCloudImageSizeCache = array();