Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/main/classes/general/ |
Upload File : |
<?php /** * Bitrix Framework * @package bitrix * @subpackage main * @copyright 2001-2024 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__); class CFile { 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, [ "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 && 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 || ($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"]) && isset($arFile["old_file"]) && 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) { 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; $connection = \Bitrix\Main\Application::getConnection(); $connection->lock('b_file', -1); $io = CBXVirtualIo::GetInstance(); $bExternalStorage = false; foreach (GetModuleEvents("main", "OnFileSave", true) as $arEvent) { if (ExecuteModuleEventEx($arEvent, [&$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 && 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 || ($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(); } 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"] <> '') { $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) { // 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 (str_starts_with($arFile["type"], "image/") && $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([ "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 ($original !== null) { //save information about the duplicate for future use (on deletion) static::AddDuplicate($original->getFileId(), $NEW_IMAGE_ID, false); } $connection->unlock('b_file'); 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; $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()) { $connection->lock('b_file', -1); Internal\FileHashTable::delete($duplicate->getId()); $duplicatePath = '/' . $duplicate->getSubdir() . '/' . $duplicate->getFileName(); if ($originalPath == $duplicatePath) { $connection->unlock('b_file'); continue; } $cancel = false; foreach (GetModuleEvents('main', 'OnBeforeFileDeleteDuplicate', true) as $event) { $cancel = ExecuteModuleEventEx($event, [$original->getFile(), $duplicate]); if ($cancel) { break; } } if ($cancel) { $connection->unlock('b_file'); 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, [$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(); } } } } $connection->unlock('b_file'); } /****************************** 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, [$arFields]); } return $fileId; } public static function Delete($ID) { $ID = intval($ID); if ($ID <= 0) { return; } $connection = Main\Application::getConnection(); $connection->lock('b_file', -1); $res = static::GetByID($ID, true); if ($res = $res->Fetch()) { $delete = static::processDuplicates($ID); if ($delete === self::DELETE_NONE) { //can't delete the file - duplicates found $connection->unlock('b_file'); 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, [$res]); } } if ($delete & self::DELETE_DB) { foreach (GetModuleEvents("main", "OnFileDelete", true) as $arEvent) { ExecuteModuleEventEx($arEvent, [$res]); } Internal\FileHashTable::delete($ID); // recursion inside static::processVersions($ID); $connection->query("DELETE FROM b_file WHERE ID = {$ID}"); static::CleanCache($ID); } /****************************** QUOTA ******************************/ if ($delete_size > 0 && COption::GetOptionInt("main", "disk_space") > 0) { CDiskQuota::updateDiskQuota("file", $delete_size, "delete"); } /****************************** QUOTA ******************************/ } $connection->unlock('b_file'); } 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']); } } 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 = [], $arFilter = []) { global $DB; $arSqlSearch = []; $arSqlOrder = []; $strSqlSearch = ""; if (is_array($arFilter)) { foreach ($arFilter as $key => $val) { $key = strtoupper($key); $strOperation = ''; if (str_starts_with($key, "@")) { $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 = [ "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] = []; } 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, [&$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 = ["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 = [], $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 = ["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 = [$mimeType]; } $goodMime = false; foreach ($mimeType as $strMimeType) { if (str_starts_with($arFile["type"], $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> 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>"+ "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 [ "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, ['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" /> </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"]); $intWidth = $arImgParams["WIDTH"]; $intHeight = $arImgParams["HEIGHT"]; $strAlt = $arImgParams["ALT"]; if (!$sParams) { $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"]); $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 = []; 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, [$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 (!is_scalar($path) || $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, [$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("#^(ftps?://|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 ($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 = []; } 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"]] = [$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 [ "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; static $cache = []; $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 = [ ["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, [ $file, [$arSize, $resizeType, [], false, $arFilters, $bImmediate], &$callbackData, &$bNeedResize, &$sourceImageFile, &$cacheImageFileTmp, ])) { break; } } if ($bNeedResize && static::ResizeImageFile($sourceImageFile, $cacheImageFileTmp, $arSize, $resizeType, [], $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, [ $file, [$arSize, $resizeType, [], 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] = [ "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) { // Ignore a E_WARNING Error } } } try { @rmdir($io->GetPhysicalName($d->GetPathWithName())); } catch (\ErrorException) { // Ignore a E_WARNING Error } return $delete_size; } /** * @param $filename * @return false|resource * @deprecated Use imagecreatefrombmp() */ public static function ImageCreateFromBMP($filename) { return imagecreatefrombmp($filename); } /** * @param $sourceImageWidth * @param $sourceImageHeight * @param $arSize * @param $resizeType * @param $bNeedCreatePicture * @param $arSourceSize * @param $arDestinationSize * @deprecated Use \Bitrix\Main\File\Image\Rectangle::resize() */ 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(), ]; } /** * @return bool * @deprecated Always returns true. */ public static function IsGD2() { return true; } public static function ResizeImageFile($sourceFile, $destinationFile, $arSize, $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL, $arWaterMark = [], $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 = []; } 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) { $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; } /** * @param $picture * @param $arFilter * @return bool * @deprecated Use \Bitrix\Main\File\Image */ 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; } /** * @param $picture * @deprecated Use \Bitrix\Main\File\Image */ public static function ImageFlipHorizontal($picture) { //prevents destroing outside the function static $engine; $engine = new File\Image\Gd(); $engine->setResource($picture); $engine->flipHorizontal(); } /** * @param $orientation * @param $sourceImage * @return false|resource * @deprecated Use \Bitrix\Main\File\Image::autoRotate() */ 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 = []) { $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(["\n", "\r"], '', $name); if ($attachment_name) { $attachment_name = str_replace(["\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 ((str_starts_with($filename, '/')) && !$fromTemp) { $file = new IO\File($_SERVER['DOCUMENT_ROOT'] . $filename); } elseif (isset($arFile['tmp_name'])) { $file = new IO\File($arFile['tmp_name']); } if ((str_starts_with($filename, '/')) && ($file instanceof IO\File)) { try { $src = $file->open(IO\FileStreamOpenMode::READ); } catch (IO\IoException) { return false; } } else { if (!$fastDownload) { $src = new Web\HttpClient(); } elseif (intval($arFile['HANDLER_ID']) > 0) { $fromClouds = true; } } $APPLICATION->RestartBuffer(); $APPLICATION->EndBufferContentMan(); $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 = strpos($_SERVER["HTTP_RANGE"] ?? '', "="); if (intval($p) > 0) { $bytes = substr($_SERVER["HTTP_RANGE"], $p + 1); $p = strpos($bytes, "-"); if ($p !== false) { $cur_pos = (float)substr($bytes, 0, $p); $size = (float)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"])); } $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); $translitName = CUtil::translit($attachment_name, LANGUAGE_ID, [ "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'); } // $p shows that we are sending partial content (range request) if ($p) { $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); // 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); } } 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(); } /** * @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 * @deprecated Use \Bitrix\Main\File\Image. */ public static function Watermark($obj, $Params) { if ($Params['type'] == 'text') { $result = static::WatermarkText($obj, $Params); } else { $result = static::WatermarkImage($obj, $Params); }; return $result; } /** * @param $obj * @param array $Params * @return bool * @deprecated Use \Bitrix\Main\File\Image::drawWatermark() */ public static function WatermarkText($obj, $Params = []) { //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. * @param $obj * @param array $Params * file - abs path to file * alpha_level - opacity * position - of the watermark * @return bool * @deprecated Use \Bitrix\Main\File\Image::drawWatermark() */ public static function WatermarkImage($obj, $Params = []) { //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; } /** * @param string $path * @return false|resource * @deprecated Use \Bitrix\Main\File\Image */ public static function CreateImage($path) { $image = new File\Image\Gd($path); if ($image->load()) { return $image->getResource(); } return false; } /** * @param $src * @return array * @deprecated Use \Bitrix\Main\File\Image::getExifData() */ public static function ExtractImageExif($src) { return (new File\Image($src))->getExifData(); } /** * @param $contentType * @return string * @deprecated Use Web\MimeType::normalize() */ 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; } /** * @param string $path * @param bool $bPhysicalName * @param bool $flashEnabled * @return array|false * @deprecated Use \Bitrix\Main\File\Image::getInfo() */ 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; } 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 = [];