1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
file.php
См. документацию.
1<?php
2
9
10use Bitrix\Main;
21
22IncludeModuleLangFile(__FILE__);
23
24class CFile
25{
26 protected const CACHE_DIR = 'b_file';
27 protected const DELETE_NONE = 0x00;
28 protected const DELETE_FILE = 0x01;
29 protected const DELETE_DB = 0x02;
30 protected const DELETE_ALL = 0x03;
31
32 public static function SaveForDB(&$arFields, $field, $strSavePath)
33 {
34 $arFile = $arFields[$field] ?? null;
35 if (isset($arFile) && is_array($arFile))
36 {
37 if (
38 (isset($arFile["name"]) && $arFile["name"] <> '')
39 || (isset($arFile["del"]) && $arFile["del"] <> '')
40 || array_key_exists("description", $arFile)
41 )
42 {
43 $res = static::SaveFile($arFile, $strSavePath);
44 if ($res !== false)
45 {
46 $arFields[$field] = (intval($res) > 0 ? $res : false);
47 return true;
48 }
49 }
50 }
51 unset($arFields[$field]);
52 return false;
53 }
54
55 public static function checkForDb($arFields, $field)
56 {
57 if (isset($arFields[$field]) && is_array($arFields[$field]))
58 {
59 $arFile = $arFields[$field];
60
61 if ($arFile["name"] == "")
62 {
63 return "";
64 }
65
66 $fileName = self::transformName($arFile["name"]);
67 return self::validateFile($fileName, $arFile);
68 }
69 else
70 {
71 return "";
72 }
73 }
74
75 protected static function transformName($name, $forceRandom = false, $bSkipExt = false)
76 {
77 //safe filename without path
79
80 $originalName = ($forceRandom != true && COption::GetOptionString("main", "save_original_file_name", "N") == "Y");
81 if ($originalName)
82 {
83 //transforming original name:
84
85 //transliteration
86 if (COption::GetOptionString("main", "translit_original_file_name", "N") == "Y")
87 {
88 $fileName = CUtil::translit($fileName, LANGUAGE_ID, [
89 "max_len" => 1024,
90 "safe_chars" => ".",
91 "replace_space" => '-',
92 "change_case" => false,
93 ]);
94 }
95
96 //replace invalid characters
97 if (COption::GetOptionString("main", "convert_original_file_name", "Y") == "Y")
98 {
100 $fileName = $io->RandomizeInvalidFilename($fileName);
101 }
102 }
103
104 //.jpe is not image type on many systems
105 if (!$bSkipExt && strtolower(GetFileExtension($fileName)) == "jpe")
106 {
107 $fileName = substr($fileName, 0, -4) . ".jpg";
108 }
109
110 //double extension vulnerability
112
113 if (!$originalName)
114 {
115 //name is randomly generated
116 $fileName = Security\Random::getString(32) . ($bSkipExt || ($ext = GetFileExtension($fileName)) == '' ? '' : "." . $ext);
117 }
118
119 return $fileName;
120 }
121
122 protected static function validateFile($strFileName, $arFile)
123 {
124 if ($strFileName == '')
125 {
126 return GetMessage("FILE_BAD_FILENAME");
127 }
128
130 if (!$io->ValidateFilenameString($strFileName))
131 {
132 return GetMessage("MAIN_BAD_FILENAME1");
133 }
134
135 if (mb_strlen($strFileName) > 255)
136 {
137 return GetMessage("MAIN_BAD_FILENAME_LEN");
138 }
139
140 //check .htaccess etc.
141 if (IsFileUnsafe($strFileName))
142 {
143 return GetMessage("FILE_BAD_TYPE");
144 }
145
146 //nginx returns octet-stream for .jpg
147 if (GetFileNameWithoutExtension($strFileName) == '')
148 {
149 return GetMessage("FILE_BAD_FILENAME");
150 }
151
152 if (COption::GetOptionInt("main", "disk_space") > 0)
153 {
154 $quota = new CDiskQuota();
155 if (!$quota->checkDiskQuota($arFile))
156 {
157 return GetMessage("FILE_BAD_QUOTA");
158 }
159 }
160
161 return "";
162 }
163
164 public static function SaveFile($arFile, $strSavePath, $forceRandom = false, $skipExtension = false, $dirAdd = '')
165 {
166 $strFileName = GetFileName($arFile["name"] ?? ''); /* filename.gif */
167
168 if (isset($arFile["del"]) && $arFile["del"] <> '')
169 {
170 static::Delete($arFile["old_file"] ?? 0);
171 if ($strFileName == '')
172 {
173 return "NULL";
174 }
175 }
176
177 if (!isset($arFile["name"]) || $arFile["name"] == '')
178 {
179 if (isset($arFile["description"]) && isset($arFile["old_file"]) && intval($arFile["old_file"]) > 0)
180 {
181 static::UpdateDesc($arFile["old_file"], $arFile["description"]);
182 }
183 return false;
184 }
185
186 if (isset($arFile["content"]))
187 {
188 if (!isset($arFile["size"]))
189 {
190 $arFile["size"] = strlen($arFile["content"]);
191 }
192 }
193 else
194 {
195 try
196 {
197 $file = new IO\File(IO\Path::convertPhysicalToLogical($arFile["tmp_name"]));
198 $arFile["size"] = $file->getSize();
199 }
200 catch (IO\IoException)
201 {
202 if (!isset($arFile["size"]) || !is_int($arFile["size"]))
203 {
204 $arFile["size"] = 0;
205 }
206 }
207 }
208
209 $arFile["ORIGINAL_NAME"] = $strFileName;
210
211 //translit, replace unsafe chars, etc.
212 $strFileName = self::transformName($strFileName, $forceRandom, $skipExtension);
213
214 //transformed name must be valid, check disk quota, etc.
215 if (self::validateFile($strFileName, $arFile) !== "")
216 {
217 return false;
218 }
219
220 $arFile["type"] = Web\MimeType::normalize($arFile["type"]);
221
222 $original = null;
223
225 $connection->lock('b_file', -1);
226
228
229 $bExternalStorage = false;
230 foreach (GetModuleEvents("main", "OnFileSave", true) as $arEvent)
231 {
232 if (ExecuteModuleEventEx($arEvent, [&$arFile, $strFileName, $strSavePath, $forceRandom, $skipExtension, $dirAdd]))
233 {
234 $bExternalStorage = true;
235 break;
236 }
237 }
238
239 if (!$bExternalStorage)
240 {
241 // we should keep number of files in a folder below 10,000
242 // three chars from md5 give us 4096 subdirs
243
244 $upload_dir = COption::GetOptionString("main", "upload_dir", "upload");
245
246 if (!$forceRandom && COption::GetOptionString("main", "save_original_file_name", "N") == "Y")
247 {
248 //original name
249 $subdir = $dirAdd;
250 if ($subdir == '')
251 {
252 while (true)
253 {
254 $random = Security\Random::getString(32);
255 $subdir = substr(md5($random), 0, 3) . "/" . $random;
256
257 if (!$io->FileExists($_SERVER["DOCUMENT_ROOT"] . "/" . $upload_dir . "/" . $strSavePath . "/" . $subdir . "/" . $strFileName))
258 {
259 break;
260 }
261 }
262 }
263 $strSavePath = rtrim($strSavePath, "/") . "/" . $subdir;
264 }
265 else
266 {
267 //random name
268 $fileExtension = ($skipExtension || ($ext = GetFileExtension($strFileName)) == '' ? '' : "." . $ext);
269 while (true)
270 {
271 $subdir = substr(md5($strFileName), 0, 3);
272 $strSavePath = rtrim($strSavePath, "/") . "/" . $subdir;
273
274 if (!$io->FileExists($_SERVER["DOCUMENT_ROOT"] . "/" . $upload_dir . "/" . $strSavePath . "/" . $strFileName))
275 {
276 break;
277 }
278
279 //try the new name
280 $strFileName = Security\Random::getString(32) . $fileExtension;
281 }
282 }
283
284 $arFile["SUBDIR"] = $strSavePath;
285 $arFile["FILE_NAME"] = $strFileName;
286
287 $dirName = $_SERVER["DOCUMENT_ROOT"] . "/" . $upload_dir . "/" . $strSavePath . "/";
288 $physicalFileName = $io->GetPhysicalName($dirName . $strFileName);
289
290 CheckDirPath($dirName);
291
292 if (is_set($arFile, "content"))
293 {
294 if (file_put_contents($physicalFileName, $arFile["content"]) === false)
295 {
296 return false;
297 }
298 }
299 else
300 {
301 if (!copy($arFile["tmp_name"], $physicalFileName) && !move_uploaded_file($arFile["tmp_name"], $physicalFileName))
302 {
303 return false;
304 }
305 }
306
307 if (isset($arFile["old_file"]))
308 {
309 static::Delete($arFile["old_file"]);
310 }
311
312 @chmod($physicalFileName, BX_FILE_PERMISSIONS);
313
314 //flash is not an image
315 $flashEnabled = !static::IsImage($arFile["ORIGINAL_NAME"], $arFile["type"]);
316
317 $image = new File\Image($physicalFileName);
318
319 $imgInfo = $image->getInfo($flashEnabled);
320
321 if ($imgInfo)
322 {
323 $arFile["WIDTH"] = $imgInfo->getWidth();
324 $arFile["HEIGHT"] = $imgInfo->getHeight();
325 }
326 else
327 {
328 $arFile["WIDTH"] = 0;
329 $arFile["HEIGHT"] = 0;
330 }
331
332 //calculate a hash for the control of duplicates
333 $arFile["FILE_HASH"] = static::CalculateHash($physicalFileName, $arFile["size"]);
334
335 //control of duplicates
336 if ($arFile["FILE_HASH"] <> '')
337 {
338 $original = static::FindDuplicate($arFile["size"], $arFile["FILE_HASH"]);
339
340 if ($original !== null)
341 {
342 //points to the original's physical path
343 $arFile["SUBDIR"] = $original->getFile()->getSubdir();
344 $arFile["FILE_NAME"] = $original->getFile()->getFileName();
345
346 $originalPath = $_SERVER["DOCUMENT_ROOT"] . "/" . $upload_dir . "/" . $arFile["SUBDIR"] . "/" . $arFile["FILE_NAME"];
347
348 if ($physicalFileName <> $io->GetPhysicalName($originalPath))
349 {
350 unlink($physicalFileName);
351 try
352 {
353 rmdir($io->GetPhysicalName($dirName));
354 }
355 catch (\ErrorException)
356 {
357 // Ignore a E_WARNING Error
358 }
359 }
360 }
361 }
362 }
363 else
364 {
365 //from clouds
366 if (isset($arFile["original_file"]) && $arFile["original_file"] instanceof Internal\EO_FileHash)
367 {
368 $original = $arFile["original_file"];
369 }
370 }
371
372 if ($arFile["WIDTH"] == 0 || $arFile["HEIGHT"] == 0)
373 {
374 //mock image because we got false from CFile::GetImageSize()
375 if (str_starts_with($arFile["type"], "image/") && $arFile["type"] <> 'image/svg+xml')
376 {
377 $arFile["type"] = "application/octet-stream";
378 }
379 }
380
381 /****************************** QUOTA ******************************/
382 if (COption::GetOptionInt("main", "disk_space") > 0 && $original === null)
383 {
384 CDiskQuota::updateDiskQuota("file", $arFile["size"], "insert");
385 }
386 /****************************** QUOTA ******************************/
387
388 $NEW_IMAGE_ID = static::DoInsert([
389 "HEIGHT" => $arFile["HEIGHT"],
390 "WIDTH" => $arFile["WIDTH"],
391 "FILE_SIZE" => $arFile["size"],
392 "CONTENT_TYPE" => $arFile["type"],
393 "SUBDIR" => $arFile["SUBDIR"],
394 "FILE_NAME" => $arFile["FILE_NAME"],
395 "MODULE_ID" => $arFile["MODULE_ID"] ?? '',
396 "ORIGINAL_NAME" => $arFile["ORIGINAL_NAME"],
397 "DESCRIPTION" => ($arFile["description"] ?? ''),
398 "HANDLER_ID" => ($arFile["HANDLER_ID"] ?? ''),
399 "EXTERNAL_ID" => ($arFile["external_id"] ?? md5(mt_rand())),
400 "FILE_HASH" => ($original === null ? $arFile["FILE_HASH"] : ''),
401 ]);
402
403 if ($original !== null)
404 {
405 //save information about the duplicate for future use (on deletion)
406 static::AddDuplicate($original->getFileId(), $NEW_IMAGE_ID, false);
407 }
408
409 $connection->unlock('b_file');
410
411 static::CleanCache($NEW_IMAGE_ID);
412
413 return $NEW_IMAGE_ID;
414 }
415
422 protected static function CalculateHash($file, $size)
423 {
424 $hash = '';
425 if ($size > 0 && COption::GetOptionString('main', 'control_file_duplicates', 'N') === 'Y')
426 {
427 $maxSize = (int)COption::GetOptionString('main', 'duplicates_max_size', '100') * 1024 * 1024; //Mbytes
428 if ($size <= $maxSize || $maxSize === 0)
429 {
430 $hash = hash_file("md5", $file);
431 }
432 }
433 return $hash;
434 }
435
442 public static function FindDuplicate($size, $hash, $handlerId = null)
443 {
445 ->where("FILE_SIZE", $size)
446 ->where("FILE_HASH", $hash)
447 ;
448
449 if ($handlerId !== null)
450 {
451 $filter->where("FILE.HANDLER_ID", $handlerId);
452 }
453 else
454 {
456 ->logic('or')
457 ->where('FILE.HANDLER_ID', '')
458 ->whereNull('FILE.HANDLER_ID')
459 );
460 }
461
463 ->addSelect("FILE.*")
464 ->where($filter)
465 ->addOrder("FILE_ID")
466 ->setLimit(1)
467 ->fetchObject()
468 ;
469 }
470
482 public static function AddDuplicate($originalId, $duplicateId = null, bool $resolvePossibleOriginCycle = true)
483 {
484 if ($duplicateId === null)
485 {
486 $duplicateId = $originalId;
487 }
488
489 if ($resolvePossibleOriginCycle || $originalId == $duplicateId)
490 {
491 //possibly there is the original already for the file
493 ->addSelect("ORIGINAL_ID")
494 ->where("DUPLICATE_ID", $originalId)
495 ->fetch()
496 ;
497
498 if ($original)
499 {
500 $originalId = $original["ORIGINAL_ID"];
501 }
502 }
503
504 $updateFields = [
505 "COUNTER" => new Main\DB\SqlExpression(Internal\FileDuplicateTable::getTableName() . '.?# + 1', 'COUNTER'),
506 ];
507
508 $insertFields = [
509 "DUPLICATE_ID" => $duplicateId,
510 "ORIGINAL_ID" => $originalId,
511 ];
512
513 Internal\FileDuplicateTable::merge($insertFields, $updateFields);
514 }
515
521 public static function DeleteDuplicates($originalId, array $duplicteIds)
522 {
524 $helper = $connection->getSqlHelper();
525
527 'select' => ['FILE_SIZE', 'FILE_HASH', 'FILE.*'],
528 'filter' => ['=FILE_ID' => $originalId],
529 ])->fetchObject();
530 if (!$original)
531 {
532 return;
533 }
534
535 $originalPath = '/' . $original->getFile()->getSubdir() . '/' . $original->getFile()->getFileName();
536
538 $uploadDir = COption::GetOptionString("main", "upload_dir", "upload");
539 $deleteSize = 0;
540
541 $fileList = \Bitrix\Main\FileTable::getList([
542 'select' => ['ID', 'FILE_SIZE', 'SUBDIR', 'FILE_NAME'],
543 'filter' => [
544 '=ID' => $duplicteIds,
545 '=HANDLER_ID' => $original->getFile()->getHandlerId(),
546 ],
547 'order' => [
548 'ID' => 'ASC',
549 ],
550 ]);
551 while ($duplicate = $fileList->fetchObject())
552 {
553 $connection->lock('b_file', -1);
554
555 Internal\FileHashTable::delete($duplicate->getId());
556
557 $duplicatePath = '/' . $duplicate->getSubdir() . '/' . $duplicate->getFileName();
558 if ($originalPath == $duplicatePath)
559 {
560 $connection->unlock('b_file');
561 continue;
562 }
563
564 $cancel = false;
565 foreach (GetModuleEvents('main', 'OnBeforeFileDeleteDuplicate', true) as $event)
566 {
567 $cancel = ExecuteModuleEventEx($event, [$original->getFile(), $duplicate]);
568 if ($cancel)
569 {
570 break;
571 }
572 }
573 if ($cancel)
574 {
575 $connection->unlock('b_file');
576 continue;
577 }
578
579 static::AddDuplicate($originalId, $duplicate->getId(), false);
580
581 $update = $helper->prepareUpdate('b_file', [
582 'SUBDIR' => $original->getFile()->getSubdir(),
583 'FILE_NAME' => $original->getFile()->getFileName(),
584 ]);
585 $ddl = 'UPDATE b_file SET ' . $update[0] . 'WHERE ID = ' . $duplicate->getId();
586 $connection->queryExecute($ddl);
587
588 static::cleanCache($duplicate->getId());
589
590 $isExternal = false;
591 foreach (GetModuleEvents('main', 'OnAfterFileDeleteDuplicate', true) as $event)
592 {
593 $isExternal = ExecuteModuleEventEx($event, [$original->getFile(), $duplicate]) || $isExternal;
594 }
595
596 if (!$isExternal)
597 {
598 $dname = $_SERVER["DOCUMENT_ROOT"] . '/' . $uploadDir . '/' . $duplicate->getSubdir();
599 $fname = $dname . '/' . $duplicate->getFileName();
600
601 $file = $io->GetFile($fname);
602 if ($file->isExists() && $file->unlink())
603 {
604 $deleteSize += $duplicate->getFileSize();
605 }
606
607 $directory = $io->GetDirectory($dname);
608 if ($directory->isExists() && $directory->isEmpty())
609 {
610 if ($directory->rmdir())
611 {
612 $parent = $io->GetDirectory(GetDirPath($dname));
613 if ($parent->isExists() && $parent->isEmpty())
614 {
615 $parent->rmdir();
616 }
617 }
618 }
619 }
620
621 $connection->unlock('b_file');
622 }
623
624 /****************************** QUOTA ******************************/
625 if ($deleteSize > 0 && COption::GetOptionInt("main", "disk_space") > 0)
626 {
627 CDiskQuota::updateDiskQuota("file", $deleteSize, "delete");
628 }
629 /****************************** QUOTA ******************************/
630 }
631
632 public static function CloneFile(int $fileId): ?int
633 {
634 $originalFile = static::GetByID($fileId)->Fetch();
635 if (!$originalFile)
636 {
637 return null;
638 }
639
640 $originalFile['FILE_HASH'] = '';
641
642 $cloneId = static::DoInsert($originalFile);
643
644 static::AddDuplicate($fileId, $cloneId);
645 static::CleanCache($cloneId);
646
647 return $cloneId;
648 }
649
650 public static function DoInsert($arFields)
651 {
652 global $DB;
653
654 $size = round(floatval($arFields["FILE_SIZE"]));
655
656 $strSql =
657 "INSERT INTO b_file(
658 TIMESTAMP_X
659 ,MODULE_ID
660 ,HEIGHT
661 ,WIDTH
662 ,FILE_SIZE
663 ,CONTENT_TYPE
664 ,SUBDIR
665 ,FILE_NAME
666 ,ORIGINAL_NAME
667 ,DESCRIPTION
668 ,HANDLER_ID
669 ,EXTERNAL_ID
670 ) VALUES (
671 " . $DB->GetNowFunction() . "
672 ,'" . $DB->ForSQL($arFields["MODULE_ID"], 50) . "'
673 ," . intval($arFields["HEIGHT"]) . "
674 ," . intval($arFields["WIDTH"]) . "
675 ," . $size . "
676 ,'" . $DB->ForSql($arFields["CONTENT_TYPE"], 255) . "'
677 ,'" . $DB->ForSql($arFields["SUBDIR"], 255) . "'
678 ,'" . $DB->ForSQL($arFields["FILE_NAME"], 255) . "'
679 ,'" . $DB->ForSql($arFields["ORIGINAL_NAME"], 255) . "'
680 ,'" . $DB->ForSQL($arFields["DESCRIPTION"], 255) . "'
681 ," . ($arFields["HANDLER_ID"] ? "'" . $DB->ForSql($arFields["HANDLER_ID"], 50) . "'" : "null") . "
682 ," . ($arFields["EXTERNAL_ID"] != "" ? "'" . $DB->ForSql($arFields["EXTERNAL_ID"], 50) . "'" : "null") . "
683 )";
684 $DB->Query($strSql);
685 $fileId = $DB->LastID();
686
687 //store the file hash for duplicates search
688 if ($arFields["FILE_HASH"] <> '')
689 {
691 "FILE_ID" => $fileId,
692 "FILE_SIZE" => $size,
693 "FILE_HASH" => $arFields["FILE_HASH"],
694 ]);
695 }
696
697 $arFields["ID"] = $fileId;
698 foreach (GetModuleEvents("main", "OnAfterFileSave", true) as $arEvent)
699 {
700 ExecuteModuleEventEx($arEvent, [$arFields]);
701 }
702
703 return $fileId;
704 }
705
706 public static function Delete($ID)
707 {
708 $ID = intval($ID);
709
710 if ($ID <= 0)
711 {
712 return;
713 }
714
716 $connection->lock('b_file', -1);
717
718 $res = static::GetByID($ID, true);
719
720 if ($res = $res->Fetch())
721 {
722 $delete = static::processDuplicates($ID);
723
724 if ($delete === self::DELETE_NONE)
725 {
726 //can't delete the file - duplicates found
727 $connection->unlock('b_file');
728 return;
729 }
730
731 $delete_size = 0;
732
733 if ($delete & self::DELETE_FILE)
734 {
735 $upload_dir = COption::GetOptionString("main", "upload_dir", "upload");
736 $dname = $_SERVER["DOCUMENT_ROOT"] . "/" . $upload_dir . "/" . $res["SUBDIR"];
737 $fname = $dname . "/" . $res["FILE_NAME"];
738
740
741 $file = $io->GetFile($fname);
742 if ($file->isExists() && $file->unlink())
743 {
744 $delete_size += $res["FILE_SIZE"];
745 }
746
747 $delete_size += static::ResizeImageDelete($res);
748
749 $directory = $io->GetDirectory($dname);
750 if ($directory->isExists() && $directory->isEmpty())
751 {
752 if ($directory->rmdir())
753 {
754 $parent = $io->GetDirectory(GetDirPath($dname));
755 if ($parent->isExists() && $parent->isEmpty())
756 {
757 $parent->rmdir();
758 }
759 }
760 }
761
762 foreach (GetModuleEvents("main", "OnPhysicalFileDelete", true) as $arEvent)
763 {
764 ExecuteModuleEventEx($arEvent, [$res]);
765 }
766 }
767
768 if ($delete & self::DELETE_DB)
769 {
770 foreach (GetModuleEvents("main", "OnFileDelete", true) as $arEvent)
771 {
772 ExecuteModuleEventEx($arEvent, [$res]);
773 }
774
776
777 // recursion inside
778 static::processVersions($ID);
779
780 $connection->query("DELETE FROM b_file WHERE ID = {$ID}");
781
782 static::CleanCache($ID);
783 }
784
785 /****************************** QUOTA ******************************/
786 if ($delete_size > 0 && COption::GetOptionInt("main", "disk_space") > 0)
787 {
788 CDiskQuota::updateDiskQuota("file", $delete_size, "delete");
789 }
790 /****************************** QUOTA ******************************/
791 }
792
793 $connection->unlock('b_file');
794 }
795
796 protected static function processDuplicates($ID)
797 {
798 $result = self::DELETE_ALL;
799
800 //Part 1: the file is a duplicate of another file, including referenses to itself
802 ->addSelect("*")
803 ->where("DUPLICATE_ID", $ID)
804 ->fetch()
805 ;
806
807 //Part 2: find duplicates of the file
809 ->where("ORIGINAL_ID", $ID)
810 ->setLimit(1)
811 ->fetch()
812 ;
813
814 //Part 1
815 if ($original)
816 {
817 if ($original["COUNTER"] > 1)
818 {
819 //decrease references counter
821 [
822 "DUPLICATE_ID" => $ID,
823 "ORIGINAL_ID" => $original["ORIGINAL_ID"],
824 ],
825 [
826 "COUNTER" => new Main\DB\SqlExpression("?# - 1", "COUNTER"),
827 ]
828 );
829
830 //there are references still
831 $result = self::DELETE_NONE;
832 }
833 else
834 {
835 //delete referense
837 "DUPLICATE_ID" => $ID,
838 "ORIGINAL_ID" => $original["ORIGINAL_ID"],
839 ]);
840
841 //delete only if the file is a duplicate of *another* file
842 if ($original["DUPLICATE_ID"] <> $original["ORIGINAL_ID"])
843 {
844 if ($original["ORIGINAL_DELETED"] === "Y")
845 {
846 //try and delete the original
847 static::Delete($original["ORIGINAL_ID"]);
848 }
849
850 //there is the original somewhere, we shouldn't delete its file
851 $result = self::DELETE_DB;
852 }
853 }
854 }
855
856 //Part 2
857 if ($duplicates)
858 {
859 //mark the original as deleted for future deletion
861
862 //duplicates found, should keep the original
863 $result = self::DELETE_NONE;
864 }
865
866 return $result;
867 }
868
875 public static function AddVersion($originalId, $versionId, $metaData = [])
876 {
878 'ORIGINAL_ID' => $originalId,
879 'VERSION_ID' => $versionId,
880 ] + (empty($metaData) ? [] : [
881 'META' => $metaData,
882 ]));
883
884 static::CleanCache($originalId);
885
886 return $result;
887 }
888
889 protected static function processVersions($ID)
890 {
891 // check if the file is something's version
893 ->addSelect('*')
894 ->where('VERSION_ID', $ID)
895 ->fetch()
896 ;
897 if ($original)
898 {
899 Internal\FileVersionTable::delete(['ORIGINAL_ID' => $original['ORIGINAL_ID']]);
900 static::CleanCache($original['ORIGINAL_ID']);
901 }
902
903 // check if the file has versions
905 ->addSelect('*')
906 ->where('ORIGINAL_ID', $ID)
907 ->exec()
908 ;
909 while ($version = $versions->fetch())
910 {
911 static::Delete($version['VERSION_ID']);
912 }
913 }
914
915 public static function CleanCache($fileId)
916 {
917 if (CACHED_b_file !== false)
918 {
919 $bucket_size = (int)CACHED_b_file_bucket_size;
920 if ($bucket_size <= 0)
921 {
922 $bucket_size = 10;
923 }
924
925 $bucket = (int)($fileId / $bucket_size);
926
927 $cache = Main\Application::getInstance()->getManagedCache();
928
929 $cache->clean(self::CACHE_DIR . '01' . $bucket, self::CACHE_DIR);
930 $cache->clean(self::CACHE_DIR . '11' . $bucket, self::CACHE_DIR);
931 $cache->clean(self::CACHE_DIR . '00' . $bucket, self::CACHE_DIR);
932 $cache->clean(self::CACHE_DIR . '10' . $bucket, self::CACHE_DIR);
933 }
934 }
935
936 public static function GetFromCache($fileId, $realId = false)
937 {
938 global $DB;
939
940 $cache = Main\Application::getInstance()->getManagedCache();
941
942 $bucketSize = (int)CACHED_b_file_bucket_size;
943 if ($bucketSize <= 0)
944 {
945 $bucketSize = 10;
946 }
947
948 $bucket = (int)($fileId / $bucketSize);
949 $https = (int)Main\Context::getCurrent()->getRequest()->isHttps();
950 $cacheId = self::CACHE_DIR . $https . (int)$realId . $bucket;
951
952 if ($cache->read(CACHED_b_file, $cacheId, self::CACHE_DIR))
953 {
954 $files = $cache->get($cacheId);
955
956 if (!isset($files[$fileId]))
957 {
958 // the trail of an incomplete bucket
959 if (!is_array($files))
960 {
961 $files = [];
962 }
963
964 if ($file = static::GetFromDb($fileId, $realId)->Fetch())
965 {
966 $files[$fileId] = $file;
967 static::CleanCache($fileId);
968 }
969 }
970 }
971 else
972 {
973 $files = [];
974
975 $minId = $bucket * $bucketSize;
976 $maxId = ($bucket + 1) * $bucketSize - 1;
977
978 $sql = "
979 SELECT f.*,
980 {$DB->DateToCharFunction("f.TIMESTAMP_X")} as TIMESTAMP_X,
981 NULL as VERSION_ORIGINAL_ID, '' as META
982 FROM b_file f
983 WHERE f.ID >= {$minId}
984 AND f.ID <= {$maxId}
985 ";
986
987 if ($realId !== true)
988 {
989 $sql .= "
990 UNION
991 SELECT f.*,
992 {$DB->DateToCharFunction("f.TIMESTAMP_X")} as TIMESTAMP_X,
993 fv.ORIGINAL_ID as VERSION_ORIGINAL_ID, fv.META as META
994 FROM b_file f
995 INNER JOIN b_file_version fv ON fv.VERSION_ID = f.ID
996 WHERE fv.ORIGINAL_ID >= {$minId}
997 AND fv.ORIGINAL_ID <= {$maxId}
998 ORDER BY ID
999 ";
1000 }
1001
1002 $rs = $DB->Query($sql);
1003
1004 while ($file = $rs->fetch())
1005 {
1006 $originalId = ($file['VERSION_ORIGINAL_ID'] ?: $file["ID"]);
1007 $files[$originalId] = $file;
1008 }
1009
1010 // store SRC in cache
1011 foreach ($files as $id => $file)
1012 {
1013 $files[$id]['SRC'] = static::GetFileSRC($file);
1014 }
1015
1016 $cache->setImmediate($cacheId, $files);
1017 }
1018 return $files;
1019 }
1020
1021 public static function GetByID($fileId, $realId = false)
1022 {
1023 $fileId = (int)$fileId;
1024
1025 if (CACHED_b_file === false)
1026 {
1027 $result = static::GetFromDb($fileId, $realId);
1028 }
1029 else
1030 {
1031 $files = static::GetFromCache($fileId, $realId);
1032
1033 $result = new CDBResult;
1034 $result->InitFromArray(isset($files[$fileId]) ? [$files[$fileId]] : []);
1035 }
1036 return $result;
1037 }
1038
1039 protected static function GetFromDb($fileId, $realId)
1040 {
1041 global $DB;
1042
1043 $strSql = "
1044 SELECT f.*,
1045 {$DB->DateToCharFunction("f.TIMESTAMP_X")} as TIMESTAMP_X,
1046 NULL as VERSION_ORIGINAL_ID, '' as META
1047 FROM b_file f
1048 WHERE f.ID = {$fileId}
1049 ";
1050
1051 if ($realId !== true)
1052 {
1053 $strSql .= "
1054 UNION
1055 SELECT f.*,
1056 {$DB->DateToCharFunction("f.TIMESTAMP_X")} as TIMESTAMP_X,
1057 fv.ORIGINAL_ID as VERSION_ORIGINAL_ID, fv.META as META
1058 FROM b_file f
1059 INNER JOIN b_file_version fv ON fv.VERSION_ID = f.ID
1060 WHERE fv.ORIGINAL_ID = {$fileId}
1061 ORDER BY ID DESC
1062 LIMIT 1
1063 ";
1064 }
1065
1066 return $DB->Query($strSql);
1067 }
1068
1069 public static function GetList($arOrder = [], $arFilter = [])
1070 {
1071 global $DB;
1072 $arSqlSearch = [];
1073 $arSqlOrder = [];
1074 $strSqlSearch = "";
1075
1076 if (is_array($arFilter))
1077 {
1078 foreach ($arFilter as $key => $val)
1079 {
1080 $key = strtoupper($key);
1081
1082 $strOperation = '';
1083 if (str_starts_with($key, "@"))
1084 {
1085 $key = substr($key, 1);
1086 $strOperation = "IN";
1087 $arIn = is_array($val) ? $val : explode(',', $val);
1088 $val = '';
1089 foreach ($arIn as $v)
1090 {
1091 $val .= ($val <> '' ? ',' : '') . "'" . $DB->ForSql(trim($v)) . "'";
1092 }
1093 }
1094 else
1095 {
1096 $val = $DB->ForSql($val);
1097 }
1098
1099 if ($val == '')
1100 {
1101 continue;
1102 }
1103
1104 switch ($key)
1105 {
1106 case "MODULE_ID":
1107 case "ID":
1108 case "EXTERNAL_ID":
1109 case "SUBDIR":
1110 case "FILE_NAME":
1111 case "ORIGINAL_NAME":
1112 case "CONTENT_TYPE":
1113 case "HANDLER_ID":
1114 if ($strOperation == "IN")
1115 {
1116 $arSqlSearch[] = "f." . $key . " IN (" . $val . ")";
1117 }
1118 else
1119 {
1120 $arSqlSearch[] = "f." . $key . " = '" . $val . "'";
1121 }
1122 break;
1123 }
1124 }
1125 }
1126 if (!empty($arSqlSearch))
1127 {
1128 $strSqlSearch = " WHERE (" . implode(") AND (", $arSqlSearch) . ")";
1129 }
1130
1131 if (is_array($arOrder))
1132 {
1133 static $aCols = [
1134 "ID" => 1,
1135 "TIMESTAMP_X" => 1,
1136 "MODULE_ID" => 1,
1137 "HEIGHT" => 1,
1138 "WIDTH" => 1,
1139 "FILE_SIZE" => 1,
1140 "CONTENT_TYPE" => 1,
1141 "SUBDIR" => 1,
1142 "FILE_NAME" => 1,
1143 "ORIGINAL_NAME" => 1,
1144 "EXTERNAL_ID" => 1,
1145 ];
1146 foreach ($arOrder as $by => $ord)
1147 {
1148 $by = strtoupper($by);
1149 if (array_key_exists($by, $aCols))
1150 {
1151 $arSqlOrder[] = "f." . $by . " " . (strtoupper($ord) == "DESC" ? "DESC" : "ASC");
1152 }
1153 }
1154 }
1155 if (empty($arSqlOrder))
1156 {
1157 $arSqlOrder[] = "f.ID ASC";
1158 }
1159 $strSqlOrder = " ORDER BY " . implode(", ", $arSqlOrder);
1160
1161 $strSql =
1162 "SELECT f.*, " . $DB->DateToCharFunction("f.TIMESTAMP_X") . " as TIMESTAMP_X " .
1163 "FROM b_file f " .
1164 $strSqlSearch .
1165 $strSqlOrder;
1166
1167 $res = $DB->Query($strSql);
1168
1169 return $res;
1170 }
1171
1172 public static function GetFileSRC($file, $uploadDir = false, $external = true)
1173 {
1174 $src = '';
1175 if ($external)
1176 {
1177 foreach (GetModuleEvents('main', 'OnGetFileSRC', true) as $event)
1178 {
1179 $src = ExecuteModuleEventEx($event, [$file]);
1180 if ($src)
1181 {
1182 break;
1183 }
1184 }
1185 }
1186
1187 if (!$src)
1188 {
1189 if ($uploadDir === false)
1190 {
1191 $uploadDir = COption::GetOptionString('main', 'upload_dir', 'upload');
1192 }
1193
1194 $src = '/' . $uploadDir . '/' . $file['SUBDIR'] . '/' . $file['FILE_NAME'];
1195
1196 $src = str_replace('//', '/', $src);
1197
1198 if (defined("BX_IMG_SERVER"))
1199 {
1200 $src = BX_IMG_SERVER . $src;
1201 }
1202 }
1203
1204 return $src;
1205 }
1206
1207 public static function GetFileArray($fileId, $uploadDir = false)
1208 {
1209 if (!is_array($fileId) && intval($fileId) > 0)
1210 {
1211 $file = static::GetByID($fileId)->Fetch();
1212
1213 if ($file)
1214 {
1215 if (!isset($file['SRC']) || $uploadDir !== false)
1216 {
1217 $file['SRC'] = static::GetFileSRC($file, $uploadDir);
1218 }
1219
1220 return $file;
1221 }
1222 }
1223 return false;
1224 }
1225
1226 public static function ConvertFilesToPost($source, &$target, $field = false)
1227 {
1228 if ($field === false)
1229 {
1230 foreach ($source as $field => $sub_source)
1231 {
1232 self::ConvertFilesToPost($sub_source, $target, $field);
1233 }
1234 }
1235 else
1236 {
1237 foreach ($source as $id => $sub_source)
1238 {
1239 if (!array_key_exists($id, $target))
1240 {
1241 $target[$id] = [];
1242 }
1243 if (is_array($sub_source))
1244 {
1245 self::ConvertFilesToPost($sub_source, $target[$id], $field);
1246 }
1247 else
1248 {
1249 $target[$id][$field] = $sub_source;
1250 }
1251 }
1252 }
1253 }
1254
1259 public static function CopyFile($FILE_ID, $bRegister = true, $newPath = "")
1260 {
1261 $z = static::GetByID($FILE_ID);
1262 if ($zr = $z->Fetch())
1263 {
1264 /****************************** QUOTA ******************************/
1265 if (COption::GetOptionInt("main", "disk_space") > 0)
1266 {
1267 $quota = new CDiskQuota();
1268 if (!$quota->checkDiskQuota($zr))
1269 {
1270 return false;
1271 }
1272 }
1273 /****************************** QUOTA ******************************/
1274
1275 $strNewFile = '';
1276 $bSaved = false;
1277 $bExternalStorage = false;
1278 foreach (GetModuleEvents("main", "OnFileCopy", true) as $arEvent)
1279 {
1280 if ($bSaved = ExecuteModuleEventEx($arEvent, [&$zr, $newPath]))
1281 {
1282 $bExternalStorage = true;
1283 break;
1284 }
1285 }
1286
1288
1289 if (!$bExternalStorage)
1290 {
1291 $strDirName = $_SERVER["DOCUMENT_ROOT"] . "/" . (COption::GetOptionString("main", "upload_dir", "upload"));
1292 $strDirName = rtrim(str_replace("//", "/", $strDirName), "/");
1293
1294 $zr["SUBDIR"] = trim($zr["SUBDIR"], "/");
1295 $zr["FILE_NAME"] = ltrim($zr["FILE_NAME"], "/");
1296
1297 $strOldFile = $strDirName . "/" . $zr["SUBDIR"] . "/" . $zr["FILE_NAME"];
1298
1299 if ($newPath <> '')
1300 {
1301 $strNewFile = $strDirName . "/" . ltrim($newPath, "/");
1302 }
1303 else
1304 {
1305 $strNewFile = $strDirName . "/" . $zr["SUBDIR"] . "/" . md5(uniqid(mt_rand())) . strrchr($zr["FILE_NAME"], ".");
1306 }
1307
1308 $zr["FILE_NAME"] = bx_basename($strNewFile);
1309 $zr["SUBDIR"] = mb_substr($strNewFile, mb_strlen($strDirName) + 1, -(mb_strlen(bx_basename($strNewFile)) + 1));
1310
1311 if ($newPath <> '')
1312 {
1313 CheckDirPath($strNewFile);
1314 }
1315
1316 $bSaved = copy($io->GetPhysicalName($strOldFile), $io->GetPhysicalName($strNewFile));
1317 }
1318
1319 if ($bSaved)
1320 {
1321 if ($bRegister)
1322 {
1323 $NEW_FILE_ID = static::DoInsert($zr);
1324
1325 if (COption::GetOptionInt("main", "disk_space") > 0)
1326 {
1327 CDiskQuota::updateDiskQuota("file", $zr["FILE_SIZE"], "copy");
1328 }
1329
1330 static::CleanCache($NEW_FILE_ID);
1331
1332 return $NEW_FILE_ID;
1333 }
1334 else
1335 {
1336 if (!$bExternalStorage)
1337 {
1338 return mb_substr($strNewFile, mb_strlen(rtrim($_SERVER["DOCUMENT_ROOT"], "/")));
1339 }
1340 else
1341 {
1342 return $bSaved;
1343 }
1344 }
1345 }
1346 else
1347 {
1348 return false;
1349 }
1350 }
1351 return 0;
1352 }
1353
1354 public static function UpdateDesc($ID, $desc)
1355 {
1356 global $DB;
1357 $DB->Query(
1358 "UPDATE b_file SET
1359 DESCRIPTION = '" . $DB->ForSql($desc, 255) . "',
1360 TIMESTAMP_X = " . $DB->GetNowFunction() . "
1361 WHERE ID=" . intval($ID)
1362 );
1363 static::CleanCache($ID);
1364 }
1365
1366 public static function UpdateExternalId($ID, $external_id)
1367 {
1368 global $DB;
1369 $external_id = trim($external_id);
1370 $DB->Query(
1371 "UPDATE b_file SET
1372 EXTERNAL_ID = " . ($external_id != "" ? "'" . $DB->ForSql($external_id, 50) . "'" : "null") . ",
1373 TIMESTAMP_X = " . $DB->GetNowFunction() . "
1374 WHERE ID=" . intval($ID)
1375 );
1376 static::CleanCache($ID);
1377 }
1378
1379 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)
1380 {
1381 $strReturn1 = "";
1382 if ($int_max_file_size != 0)
1383 {
1384 $strReturn1 .= "<input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"" . $int_max_file_size . "\" /> ";
1385 }
1386
1387 $strReturn1 .= ' <input name="' . $strFieldName . '" ' . $field_file . ' size="' . $int_field_size . '" type="file" />';
1388 $strReturn2 = '<span class="bx-input-file-desc">';
1389 $strDescription = "";
1390 $db_img_arr = static::GetFileArray($strImageID, $strImageStorePath);
1391
1392 if ($db_img_arr)
1393 {
1394 $strDescription = $db_img_arr["DESCRIPTION"];
1395
1396 if (($p = mb_strpos($strFieldName, "[")) > 0)
1397 {
1398 $strDelName = mb_substr($strFieldName, 0, $p) . "_del" . mb_substr($strFieldName, $p);
1399 }
1400 else
1401 {
1402 $strDelName = $strFieldName . "_del";
1403 }
1404
1405 if ($bShowNotes)
1406 {
1407 if ($bShowFilePath)
1408 {
1409 $filePath = $db_img_arr["SRC"];
1410 }
1411 else
1412 {
1413 $filePath = $db_img_arr['ORIGINAL_NAME'];
1414 }
1416 if ($io->FileExists($_SERVER["DOCUMENT_ROOT"] . $db_img_arr["SRC"]) || $db_img_arr["HANDLER_ID"])
1417 {
1418 $strReturn2 .= "<br>&nbsp;" . GetMessage("FILE_TEXT") . ": " . htmlspecialcharsEx($filePath);
1419 if (mb_strtoupper($strFileType) == "IMAGE")
1420 {
1421 $intWidth = intval($db_img_arr["WIDTH"]);
1422 $intHeight = intval($db_img_arr["HEIGHT"]);
1423 if ($intWidth > 0 && $intHeight > 0)
1424 {
1425 $strReturn2 .= "<br>&nbsp;" . GetMessage("FILE_WIDTH") . ": $intWidth";
1426 $strReturn2 .= "<br>&nbsp;" . GetMessage("FILE_HEIGHT") . ": $intHeight";
1427 }
1428 }
1429 $strReturn2 .= "<br>&nbsp;" . GetMessage("FILE_SIZE") . ": " . static::FormatSize($db_img_arr["FILE_SIZE"]);
1430 }
1431 else
1432 {
1433 $strReturn2 .= "<br>" . GetMessage("FILE_NOT_FOUND") . ": " . htmlspecialcharsEx($filePath);
1434 }
1435 }
1436 $strReturn2 .= "<br><input " . $field_checkbox . " type=\"checkbox\" name=\"" . $strDelName . "\" value=\"Y\" id=\"" . $strDelName . "\" /> <label for=\"" . $strDelName . "\">" . GetMessage("FILE_DELETE") . "</label>";
1437 }
1438
1439 $strReturn2 .= '</span>';
1440
1441 return $strReturn1 . (
1442 $description_size > 0 ?
1443 '<br><input type="text" value="' . htmlspecialcharsbx($strDescription) . '" name="' . $strFieldName . '_descr" ' . $field_text . ' size="' . $description_size . '" title="' . GetMessage("MAIN_FIELD_FILE_DESC") . '" />'
1444 : ''
1445 ) . $strReturn2;
1446 }
1447
1453 public static function FormatSize($size, $precision = 2)
1454 {
1455 static $a = ["b", "Kb", "Mb", "Gb", "Tb"];
1456
1457 $size = (float)$size;
1458 $pos = 0;
1459 while ($size >= 1024 && $pos < 4)
1460 {
1461 $size /= 1024;
1462 $pos++;
1463 }
1464 return round($size, $precision) . " " . GetMessage("FILE_SIZE_" . $a[$pos]);
1465 }
1466
1467 public static function GetImageExtensions()
1468 {
1469 return "jpg,bmp,jpeg,jpe,gif,png,webp";
1470 }
1471
1472 public static function GetFlashExtensions()
1473 {
1474 return "swf";
1475 }
1476
1477 public static function IsImage($filename, $mime_type = false)
1478 {
1479 $ext = strtolower(GetFileExtension($filename));
1480 if ($ext <> '')
1481 {
1482 if (in_array($ext, explode(",", static::GetImageExtensions())))
1483 {
1484 if ($mime_type === false || Web\MimeType::isImage($mime_type))
1485 {
1486 return true;
1487 }
1488 }
1489 }
1490 return false;
1491 }
1492
1493 public static function CheckImageFile($arFile, $iMaxSize = 0, $iMaxWidth = 0, $iMaxHeight = 0, $access_typies = [], $bForceMD5 = false, $bSkipExt = false)
1494 {
1495 if (!isset($arFile["name"]) || $arFile["name"] == "")
1496 {
1497 return "";
1498 }
1499
1500 if (empty($arFile["tmp_name"]))
1501 {
1502 return GetMessage("FILE_BAD_FILE_TYPE") . ".<br>";
1503 }
1504
1505 if (preg_match("#^(php://|phar://)#i", $arFile["tmp_name"]))
1506 {
1507 return GetMessage("FILE_BAD_FILE_TYPE") . ".<br>";
1508 }
1509
1510 $file_type = GetFileType($arFile["name"]);
1511
1512 // IMAGE by default
1513 $flashEnabled = false;
1514 if (!in_array($file_type, $access_typies))
1515 {
1516 $file_type = "IMAGE";
1517 }
1518
1519 if ($file_type == "FLASH")
1520 {
1521 $flashEnabled = true;
1522 static $flashMime = ["application/x-shockwave-flash", "application/vnd.adobe.flash.movie"];
1523 $res = static::CheckFile($arFile, $iMaxSize, $flashMime, static::GetFlashExtensions(), $bForceMD5, $bSkipExt);
1524 }
1525 else
1526 {
1527 $res = static::CheckFile($arFile, $iMaxSize, "image/", static::GetImageExtensions(), $bForceMD5, $bSkipExt);
1528 }
1529
1530 if ($res <> '')
1531 {
1532 return $res;
1533 }
1534
1535 $imgInfo = (new File\Image($arFile["tmp_name"]))->getInfo($flashEnabled);
1536
1537 if ($imgInfo)
1538 {
1539 $intWIDTH = $imgInfo->getWidth();
1540 $intHEIGHT = $imgInfo->getHeight();
1541 }
1542 else
1543 {
1544 return GetMessage("FILE_BAD_FILE_TYPE") . ".<br>";
1545 }
1546
1547 //check for dimensions
1548 if ($iMaxWidth > 0 && ($intWIDTH > $iMaxWidth || $intWIDTH == 0) || $iMaxHeight > 0 && ($intHEIGHT > $iMaxHeight || $intHEIGHT == 0))
1549 {
1550 return GetMessage("FILE_BAD_MAX_RESOLUTION") . " (" . $iMaxWidth . " * " . $iMaxHeight . " " . GetMessage("main_include_dots") . ").<br>";
1551 }
1552
1553 return null;
1554 }
1555
1556 public static function CheckFile($arFile, $intMaxSize = 0, $mimeType = false, $strExt = false, $bForceMD5 = false, $bSkipExt = false)
1557 {
1558 if ($arFile["name"] == "")
1559 {
1560 return "";
1561 }
1562
1563 //translit, replace unsafe chars, etc.
1564 $strFileName = self::transformName($arFile["name"], $bForceMD5, $bSkipExt);
1565
1566 //transformed name must be valid, check disk quota, etc.
1567 if (($error = self::validateFile($strFileName, $arFile)) <> '')
1568 {
1569 return $error;
1570 }
1571
1572 if ($intMaxSize > 0 && $arFile["size"] > $intMaxSize)
1573 {
1574 return GetMessage("FILE_BAD_SIZE") . " (" . static::FormatSize($intMaxSize) . ").";
1575 }
1576
1577 $strFileExt = '';
1578 if ($strExt)
1579 {
1580 $strFileExt = GetFileExtension($strFileName);
1581 if ($strFileExt == '')
1582 {
1583 return GetMessage("FILE_BAD_TYPE");
1584 }
1585 }
1586
1587 //Check mime type
1588 if ($mimeType !== false)
1589 {
1590 if (!is_array($mimeType))
1591 {
1592 $mimeType = [$mimeType];
1593 }
1594 $goodMime = false;
1595 foreach ($mimeType as $strMimeType)
1596 {
1597 if (str_starts_with($arFile["type"], $strMimeType))
1598 {
1599 $goodMime = true;
1600 break;
1601 }
1602 }
1603 if (!$goodMime)
1604 {
1605 return GetMessage("FILE_BAD_TYPE");
1606 }
1607 }
1608
1609 //Check extension
1610 if ($strExt === false)
1611 {
1612 return "";
1613 }
1614
1615 $IsExtCorrect = true;
1616 if ($strExt)
1617 {
1618 $IsExtCorrect = false;
1619 $tok = strtok($strExt, ",");
1620 while ($tok)
1621 {
1622 if (strtolower(trim($tok)) == strtolower($strFileExt))
1623 {
1624 $IsExtCorrect = true;
1625 break;
1626 }
1627 $tok = strtok(",");
1628 }
1629 }
1630
1631 if ($IsExtCorrect)
1632 {
1633 return "";
1634 }
1635
1636 return GetMessage("FILE_BAD_TYPE") . " (" . strip_tags($strFileExt) . ")";
1637 }
1638
1639 public static function ShowFile($iFileID, $max_file_size = 0, $iMaxW = 0, $iMaxH = 0, $bPopup = false, $sParams = false, $sPopupTitle = false, $iSizeWHTTP = 0, $iSizeHHTTP = 0)
1640 {
1641 $strResult = "";
1642
1643 $arFile = static::GetFileArray($iFileID);
1644 if ($arFile)
1645 {
1646 $max_file_size = intval($max_file_size);
1647 if ($max_file_size <= 0)
1648 {
1649 $max_file_size = 1000000000;
1650 }
1651
1652 $ct = $arFile["CONTENT_TYPE"];
1653 if ($arFile["FILE_SIZE"] <= $max_file_size && static::IsImage($arFile["SRC"], $ct))
1654 {
1655 $strResult = static::ShowImage($arFile, $iMaxW, $iMaxH, $sParams, "", $bPopup, $sPopupTitle, $iSizeWHTTP, $iSizeHHTTP);
1656 }
1657 else
1658 {
1659 $strResult = '<a href="' . htmlspecialcharsbx($arFile["SRC"]) . '" title="' . GetMessage("FILE_FILE_DOWNLOAD") . '">' . htmlspecialcharsbx($arFile["FILE_NAME"]) . '</a>';
1660 }
1661 }
1662 return $strResult;
1663 }
1664
1665 public static function DisableJSFunction($b = true)
1666 {
1667 global $SHOWIMAGEFIRST;
1668 $SHOWIMAGEFIRST = $b;
1669 }
1670
1671 public static function OutputJSImgShw()
1672 {
1673 global $SHOWIMAGEFIRST;
1674 if (!defined("ADMIN_SECTION") && $SHOWIMAGEFIRST !== true)
1675 {
1676 echo
1677 '<script>
1678function ImgShw(ID, width, height, alt)
1679{
1680 var scroll = "no";
1681 var top=0, left=0;
1682 var w, h;
1683 if(navigator.userAgent.toLowerCase().indexOf("opera") != -1)
1684 {
1685 w = document.body.offsetWidth;
1686 h = document.body.offsetHeight;
1687 }
1688 else
1689 {
1690 w = screen.width;
1691 h = screen.height;
1692 }
1693 if(width > w-10 || height > h-28)
1694 scroll = "yes";
1695 if(height < h-28)
1696 top = Math.floor((h - height)/2-14);
1697 if(width < w-10)
1698 left = Math.floor((w - width)/2-5);
1699 width = Math.min(width, w-10);
1700 height = Math.min(height, h-28);
1701 var wnd = window.open("","","scrollbars="+scroll+",resizable=yes,width="+width+",height="+height+",left="+left+",top="+top);
1702 wnd.document.write(
1703 "<html><head>"+
1704 "<"+"script>"+
1705 "function KeyPress(e)"+
1706 "{"+
1707 " if (!e) e = window.event;"+
1708 " if(e.keyCode == 27) "+
1709 " window.close();"+
1710 "}"+
1711 "</"+"script>"+
1712 "<title>"+(alt == ""? "' . GetMessage("main_js_img_title") . '":alt)+"</title></head>"+
1713 "<body topmargin=\"0\" leftmargin=\"0\" marginwidth=\"0\" marginheight=\"0\" onKeyDown=\"KeyPress(arguments[0])\">"+
1714 "<img src=\""+ID+"\" border=\"0\" alt=\""+alt+"\" />"+
1715 "</body></html>"
1716 );
1717 wnd.document.close();
1718 wnd.focus();
1719}
1720</script>';
1721
1722 $SHOWIMAGEFIRST = true;
1723 }
1724 }
1725
1726 public static function _GetImgParams($strImage, $iSizeWHTTP = 0, $iSizeHHTTP = 0)
1727 {
1729
1731
1732 if ($strImage == '')
1733 {
1734 return false;
1735 }
1736
1737 $strAlt = '';
1738 if (intval($strImage) > 0)
1739 {
1740 $db_img_arr = static::GetFileArray($strImage);
1741 if ($db_img_arr)
1742 {
1743 $strImage = $db_img_arr["SRC"];
1744 $intWidth = intval($db_img_arr["WIDTH"]);
1745 $intHeight = intval($db_img_arr["HEIGHT"]);
1746 $strAlt = $db_img_arr["DESCRIPTION"];
1747 }
1748 else
1749 {
1750 return false;
1751 }
1752 }
1753 else
1754 {
1755 if (!preg_match("#^https?://#", $strImage))
1756 {
1757 $imageInfo = (new File\Image($io->GetPhysicalName($_SERVER["DOCUMENT_ROOT"] . $strImage)))->getInfo();
1758 if ($imageInfo)
1759 {
1760 $intWidth = $imageInfo->getWidth();
1761 $intHeight = $imageInfo->getHeight();
1762 }
1763 else
1764 {
1765 return false;
1766 }
1767 }
1768 elseif (array_key_exists($strImage, $arCloudImageSizeCache))
1769 {
1770 $intWidth = $arCloudImageSizeCache[$strImage][0];
1771 $intHeight = $arCloudImageSizeCache[$strImage][1];
1772 }
1773 else
1774 {
1775 $intWidth = intval($iSizeWHTTP);
1776 $intHeight = intval($iSizeHHTTP);
1777 }
1778 }
1779
1780 return [
1781 "SRC" => $strImage,
1782 "WIDTH" => $intWidth,
1783 "HEIGHT" => $intHeight,
1784 "ALT" => $strAlt,
1785 ];
1786 }
1787
1794 public static function GetPath($img_id)
1795 {
1796 $img_id = intval($img_id);
1797 if ($img_id > 0)
1798 {
1799 $res = static::_GetImgParams($img_id);
1800 return is_array($res) ? $res["SRC"] : null;
1801 }
1802 return null;
1803 }
1804
1805 public static function ShowImage($strImage, $iMaxW = 0, $iMaxH = 0, $sParams = null, $strImageUrl = "", $bPopup = false, $sPopupTitle = false, $iSizeWHTTP = 0, $iSizeHHTTP = 0, $strImageUrlTemplate = "")
1806 {
1807 if (is_array($strImage))
1808 {
1809 $arImgParams = $strImage;
1810 $iImageID = isset($arImgParams['ID']) ? (int)$arImgParams['ID'] : 0;
1811 }
1812 else
1813 {
1814 $arImgParams = static::_GetImgParams($strImage, $iSizeWHTTP, $iSizeHHTTP);
1815 $iImageID = (int)$strImage;
1816 }
1817
1818 if (!$arImgParams)
1819 {
1820 return "";
1821 }
1822
1823 $iMaxW = (int)$iMaxW;
1824 $iMaxH = (int)$iMaxH;
1825 $intWidth = (int)$arImgParams['WIDTH'];
1826 $intHeight = (int)$arImgParams['HEIGHT'];
1827 if (
1828 $iMaxW > 0
1829 && $iMaxH > 0
1830 && ($intWidth > $iMaxW || $intHeight > $iMaxH)
1831 )
1832 {
1833 $coeff = ($intWidth / $iMaxW > $intHeight / $iMaxH ? $intWidth / $iMaxW : $intHeight / $iMaxH);
1834 $iHeight = (int)roundEx($intHeight / $coeff);
1835 $iWidth = (int)roundEx($intWidth / $coeff);
1836 }
1837 else
1838 {
1839 $coeff = 1;
1840 $iHeight = $intHeight;
1841 $iWidth = $intWidth;
1842 }
1843
1844 $strImageUrlTemplate = strval($strImageUrlTemplate);
1845 if ($strImageUrlTemplate === '' || $iImageID <= 0)
1846 {
1847 $strImage = $arImgParams['SRC'];
1848 }
1849 else
1850 {
1851 $strImage = CComponentEngine::MakePathFromTemplate($strImageUrlTemplate, ['file_id' => $iImageID]);
1852 }
1853
1854 $strImage = Uri::urnEncode($strImage);
1855
1856 if (GetFileType($strImage) == "FLASH")
1857 {
1858 $strReturn = '
1859 <object
1860 classid="clsid:D27CDB6E-AE6D-11CF-96B8-444553540000"
1861 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"
1862 id="banner"
1863 WIDTH="' . $iWidth . '"
1864 HEIGHT="' . $iHeight . '"
1865 ALIGN="">
1866 <PARAM NAME="movie" VALUE="' . htmlspecialcharsbx($strImage) . '" />
1867 <PARAM NAME="quality" VALUE="high" />
1868 <PARAM NAME="bgcolor" VALUE="#FFFFFF" />
1869 <embed
1870 src="' . htmlspecialcharsbx($strImage) . '"
1871 quality="high"
1872 bgcolor="#FFFFFF"
1873 WIDTH="' . $iWidth . '"
1874 HEIGHT="' . $iHeight . '"
1875 NAME="banner"
1876 ALIGN=""
1877 TYPE="application/x-shockwave-flash"
1878 PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer"
1879 />
1880 </object>
1881 ';
1882 }
1883 else
1884 {
1885 $strAlt = $arImgParams['ALT'] ?? ($arImgParams['DESCRIPTION'] ?? '');
1886
1887 if ($sParams === null || $sParams === false)
1888 {
1889 $sParams = 'border="0" alt="' . htmlspecialcharsEx($strAlt) . '"';
1890 }
1891 elseif (!preg_match('/(^|\\s)alt\\s*=\\s*(["\']?)(.*?)(\\2)/is', $sParams))
1892 {
1893 $sParams .= ' alt="' . htmlspecialcharsEx($strAlt) . '"';
1894 }
1895
1896 if ($coeff === 1 || !$bPopup)
1897 {
1898 $strReturn = '<img src="' . htmlspecialcharsbx($strImage) . '" ' . $sParams . ' width="' . $iWidth . '" height="' . $iHeight . '" />';
1899 }
1900 else
1901 {
1902 if ($sPopupTitle === false)
1903 {
1904 $sPopupTitle = GetMessage('FILE_ENLARGE');
1905 }
1906
1907 if ($strImageUrl <> '')
1908 {
1909 $strReturn =
1910 '<a href="' . $strImageUrl . '" title="' . htmlspecialcharsEx($sPopupTitle) . '" target="_blank">' .
1911 '<img src="' . htmlspecialcharsbx($strImage) . '" ' . $sParams . ' width="' . $iWidth . '" height="' . $iHeight . '" title="' . htmlspecialcharsEx($sPopupTitle) . '" />' .
1912 '</a>';
1913 }
1914 else
1915 {
1916 static::OutputJSImgShw();
1917
1918 $strReturn =
1919 '<a title="' . $sPopupTitle . '" ' .
1920 'onclick="ImgShw(\'' . htmlspecialcharsbx(CUtil::addslashes($strImage)) . '\', ' . $intWidth . ', ' . $intHeight . ', \'' . CUtil::addslashes(htmlspecialcharsEx(htmlspecialcharsEx($strAlt))) . '\'); return false;" ' .
1921 'href="' . htmlspecialcharsbx($strImage) . '" ' .
1922 'target="_blank"' .
1923 '>' .
1924 '<img src="' . htmlspecialcharsbx($strImage) . '" ' . $sParams . ' width="' . $iWidth . '" height="' . $iHeight . '" />' .
1925 '</a>';
1926 }
1927 }
1928 }
1929
1930 return $bPopup ? $strReturn : print_url($strImageUrl, $strReturn);
1931 }
1932
1933 public static function Show2Images($strImage1, $strImage2, $iMaxW = 0, $iMaxH = 0, $sParams = false, $sPopupTitle = false, $iSizeWHTTP = 0, $iSizeHHTTP = 0)
1934 {
1935 if (!($arImgParams = static::_GetImgParams($strImage1, $iSizeWHTTP, $iSizeHHTTP)))
1936 {
1937 return "";
1938 }
1939
1940 $strImage1 = Uri::urnEncode($arImgParams["SRC"]);
1941
1942 $intWidth = $arImgParams["WIDTH"];
1943 $intHeight = $arImgParams["HEIGHT"];
1944 $strAlt = $arImgParams["ALT"];
1945
1946 if (!$sParams)
1947 {
1948 $sParams = 'border="0" alt="' . htmlspecialcharsEx($strAlt) . '"';
1949 }
1950 elseif (!preg_match("/(^|\\s)alt\\s*=\\s*([\"']?)(.*?)(\\2)/is", $sParams))
1951 {
1952 $sParams .= ' alt="' . htmlspecialcharsEx($strAlt) . '"';
1953 }
1954
1955 if (
1956 $iMaxW > 0 && $iMaxH > 0
1957 && ($intWidth > $iMaxW || $intHeight > $iMaxH)
1958 )
1959 {
1960 $coeff = ($intWidth / $iMaxW > $intHeight / $iMaxH ? $intWidth / $iMaxW : $intHeight / $iMaxH);
1961 $iHeight = intval(roundEx($intHeight / $coeff));
1962 $iWidth = intval(roundEx($intWidth / $coeff));
1963 }
1964 else
1965 {
1966 $iHeight = $intHeight;
1967 $iWidth = $intWidth;
1968 }
1969
1970 if ($arImgParams = static::_GetImgParams($strImage2, $iSizeWHTTP, $iSizeHHTTP))
1971 {
1972 if ($sPopupTitle === false)
1973 {
1974 $sPopupTitle = GetMessage("FILE_ENLARGE");
1975 }
1976
1977 $strImage2 = Uri::urnEncode($arImgParams["SRC"]);
1978 $intWidth2 = $arImgParams["WIDTH"];
1979 $intHeight2 = $arImgParams["HEIGHT"];
1980 $strAlt2 = $arImgParams["ALT"];
1981
1982 static::OutputJSImgShw();
1983
1984 $strReturn =
1985 "<a title=\"" . $sPopupTitle . "\" onclick=\"ImgShw('" . CUtil::addslashes($strImage2) . "','" . $intWidth2 . "','" . $intHeight2 . "', '" . CUtil::addslashes(htmlspecialcharsEx(htmlspecialcharsEx($strAlt2))) . "'); return false;\" href=\"" . $strImage2 . "\" target=_blank>" .
1986 "<img src=\"" . $strImage1 . "\" " . $sParams . " width=" . $iWidth . " height=" . $iHeight . " /></a>";
1987 }
1988 else
1989 {
1990 $strReturn = "<img src=\"" . $strImage1 . "\" " . $sParams . " width=" . $iWidth . " height=" . $iHeight . " />";
1991 }
1992
1993 return $strReturn;
1994 }
1995
2005 public static function MakeFileArray($path, $mimetype = false, $skipInternal = false, $external_id = "")
2006 {
2008 $arFile = [];
2009
2010 if (intval($path) > 0)
2011 {
2012 if ($skipInternal)
2013 {
2014 return false;
2015 }
2016
2017 $res = static::GetByID($path);
2018 if ($ar = $res->Fetch())
2019 {
2020 $bExternalStorage = false;
2021 foreach (GetModuleEvents("main", "OnMakeFileArray", true) as $arEvent)
2022 {
2023 if (ExecuteModuleEventEx($arEvent, [$ar, &$arFile]))
2024 {
2025 $bExternalStorage = true;
2026 break;
2027 }
2028 }
2029
2030 if (!$bExternalStorage)
2031 {
2032 $arFile["name"] = ($ar['ORIGINAL_NAME'] <> '' ? $ar['ORIGINAL_NAME'] : $ar['FILE_NAME']);
2033 $arFile["size"] = $ar['FILE_SIZE'];
2034 $arFile["type"] = $ar['CONTENT_TYPE'];
2035 $arFile["description"] = $ar['DESCRIPTION'];
2036 $arFile["tmp_name"] = $io->GetPhysicalName(preg_replace("#[\\\\/]+#", "/", $_SERVER['DOCUMENT_ROOT'] . '/' . (COption::GetOptionString('main', 'upload_dir', 'upload')) . '/' . $ar['SUBDIR'] . '/' . $ar['FILE_NAME']));
2037 }
2038 if (!isset($arFile["external_id"]))
2039 {
2040 $arFile["external_id"] = $external_id != "" ? $external_id : $ar["EXTERNAL_ID"];
2041 }
2042 return $arFile;
2043 }
2044 }
2045
2046 $path = preg_replace("#(?<!:)[\\\\/]+#", "/", $path);
2047
2048 if (!is_scalar($path) || $path == '' || $path == "/")
2049 {
2050 return null;
2051 }
2052
2053 if (preg_match("#^(php://|phar://)#i", $path) && !preg_match("#^php://input$#i", $path))
2054 {
2055 return null;
2056 }
2057
2058 if (preg_match("#^https?://#i", $path))
2059 {
2060 $temp_path = '';
2061 $bExternalStorage = false;
2062 foreach (GetModuleEvents("main", "OnMakeFileArray", true) as $arEvent)
2063 {
2064 if (ExecuteModuleEventEx($arEvent, [$path, &$temp_path]))
2065 {
2066 $bExternalStorage = true;
2067 break;
2068 }
2069 }
2070
2071 if (!$bExternalStorage)
2072 {
2073 $http = new Web\HttpClient();
2074 $http->setPrivateIp(false);
2075 $temp_path = static::GetTempName('', 'tmp.' . md5(mt_rand()));
2076 if ($http->download($path, $temp_path))
2077 {
2078 $arFile = static::MakeFileArray($temp_path);
2079 if ($arFile)
2080 {
2081 $urlComponents = parse_url($path);
2082 if ($urlComponents && $urlComponents["path"] <> '')
2083 {
2084 $arFile["name"] = $io->GetLogicalName(bx_basename($urlComponents["path"]));
2085 }
2086 else
2087 {
2088 $arFile["name"] = $io->GetLogicalName(bx_basename($path));
2089 }
2090 }
2091 }
2092 }
2093 elseif ($temp_path)
2094 {
2095 $arFile = static::MakeFileArray($temp_path);
2096 }
2097 }
2098 elseif (preg_match("#^(ftps?://|php://input)#i", $path))
2099 {
2100 if ($fp = fopen($path, "rb"))
2101 {
2102 $content = "";
2103 while (!feof($fp))
2104 {
2105 $content .= fgets($fp, 4096);
2106 }
2107
2108 if ($content <> '')
2109 {
2110 $temp_path = static::GetTempName('', 'tmp.' . md5(mt_rand()));
2111 if (RewriteFile($temp_path, $content))
2112 {
2113 $arFile = static::MakeFileArray($temp_path);
2114 if ($arFile)
2115 {
2116 $arFile["name"] = $io->GetLogicalName(bx_basename($path));
2117 }
2118 }
2119 }
2120
2121 fclose($fp);
2122 }
2123 }
2124 else
2125 {
2126 if (!file_exists($path))
2127 {
2128 if (file_exists($_SERVER["DOCUMENT_ROOT"] . $path))
2129 {
2130 $path = $_SERVER["DOCUMENT_ROOT"] . $path;
2131 }
2132 else
2133 {
2134 return null;
2135 }
2136 }
2137
2138 if (is_dir($path))
2139 {
2140 return null;
2141 }
2142
2143 $arFile["name"] = $io->GetLogicalName(bx_basename($path));
2144 $arFile["size"] = filesize($path);
2145 $arFile["tmp_name"] = $path;
2146 $arFile["type"] = $mimetype;
2147
2148 if ($arFile["type"] == '')
2149 {
2150 $arFile["type"] = static::GetContentType($path, true);
2151 }
2152 }
2153
2154 if ($arFile["type"] == '')
2155 {
2156 $arFile["type"] = "unknown";
2157 }
2158
2159 if (!isset($arFile["external_id"]) && ($external_id != ""))
2160 {
2161 $arFile["external_id"] = $external_id;
2162 }
2163
2164 return $arFile;
2165 }
2166
2167 public static function GetTempName($dir_name = false, $file_name = '')
2168 {
2169 //accidentally $file_name can contain "?params"
2170 if (($pos = mb_strpos($file_name, "?")) !== false)
2171 {
2172 $file_name = mb_substr($file_name, 0, $pos);
2173 }
2174 return CTempFile::GetFileName($file_name);
2175 }
2176
2177 public static function ChangeSubDir($module_id, $old_subdir, $new_subdir)
2178 {
2179 global $DB;
2180
2181 if ($old_subdir != $new_subdir)
2182 {
2183 $strSql = "
2184 UPDATE b_file SET
2185 SUBDIR = REPLACE(SUBDIR,'" . $DB->ForSQL($old_subdir) . "','" . $DB->ForSQL($new_subdir) . "'),
2186 TIMESTAMP_X = " . $DB->GetNowFunction() . "
2187 WHERE MODULE_ID='" . $DB->ForSQL($module_id) . "'
2188 ";
2189
2190 if ($DB->Query($strSql))
2191 {
2192 $from = "/" . COption::GetOptionString("main", "upload_dir", "upload") . "/" . $old_subdir;
2193 $to = "/" . COption::GetOptionString("main", "upload_dir", "upload") . "/" . $new_subdir;
2194 CopyDirFiles($_SERVER["DOCUMENT_ROOT"] . $from, $_SERVER["DOCUMENT_ROOT"] . $to, true, true, true);
2195
2196 //Reset All b_file cache
2197 $cache = Main\Application::getInstance()->getManagedCache();
2198 $cache->cleanDir(self::CACHE_DIR);
2199 }
2200 }
2201 }
2202
2203 public static function ResizeImage(&$arFile, $arSize, $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL)
2204 {
2206
2207 // $arFile["tmp_name"] should contain physical filename
2208 $destinationFile = CTempFile::GetFileName(basename($arFile["tmp_name"]));
2209 $sourceFile = $io->GetLogicalName($arFile["tmp_name"]);
2210
2211 CheckDirPath($destinationFile);
2212
2213 if (static::ResizeImageFile($sourceFile, $destinationFile, $arSize, $resizeType))
2214 {
2215 $arFile["tmp_name"] = $io->GetPhysicalName($destinationFile);
2216
2217 $imageInfo = (new File\Image($arFile["tmp_name"]))->getInfo();
2218 if ($imageInfo)
2219 {
2220 $arFile["type"] = $imageInfo->getMime();
2221 }
2222 $arFile["size"] = filesize($arFile["tmp_name"]);
2223
2224 return true;
2225 }
2226
2227 return false;
2228 }
2229
2230 public static function ResizeImageDeleteCache($arFile)
2231 {
2232 $temp_dir = CTempFile::GetAbsoluteRoot() . "/";
2233 if (mb_strpos($arFile["tmp_name"], $temp_dir) === 0)
2234 {
2235 if (file_exists($arFile["tmp_name"]))
2236 {
2237 unlink($arFile["tmp_name"]);
2238 }
2239 }
2240 }
2241
2242 public static function ResizeImageGet($file, $arSize, $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL, $bInitSizes = false, $arFilters = false, $bImmediate = false, $jpgQuality = false)
2243 {
2244 if (!is_array($file) && intval($file) > 0)
2245 {
2246 $file = static::GetFileArray($file);
2247 }
2248
2249 if (!is_array($file) || !array_key_exists("FILE_NAME", $file) || $file["FILE_NAME"] == '')
2250 {
2251 return false;
2252 }
2253
2254 if ($resizeType !== BX_RESIZE_IMAGE_EXACT && $resizeType !== BX_RESIZE_IMAGE_PROPORTIONAL_ALT)
2255 {
2256 $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL;
2257 }
2258
2259 if (!is_array($arSize))
2260 {
2261 $arSize = [];
2262 }
2263 if (!array_key_exists("width", $arSize) || intval($arSize["width"]) <= 0)
2264 {
2265 $arSize["width"] = 0;
2266 }
2267 if (!array_key_exists("height", $arSize) || intval($arSize["height"]) <= 0)
2268 {
2269 $arSize["height"] = 0;
2270 }
2271 $arSize["width"] = intval($arSize["width"]);
2272 $arSize["height"] = intval($arSize["height"]);
2273
2274 $uploadDirName = COption::GetOptionString("main", "upload_dir", "upload");
2275
2276 $imageFile = "/" . $uploadDirName . "/" . $file["SUBDIR"] . "/" . $file["FILE_NAME"];
2277 $arImageSize = false;
2278 $bFilters = is_array($arFilters) && !empty($arFilters);
2279
2280 if (
2281 ($arSize["width"] <= 0 || $arSize["width"] >= $file["WIDTH"])
2282 && ($arSize["height"] <= 0 || $arSize["height"] >= $file["HEIGHT"])
2283 )
2284 {
2285 if ($bFilters)
2286 {
2287 //Only filters. Leave size unchanged
2288 $arSize["width"] = $file["WIDTH"];
2289 $arSize["height"] = $file["HEIGHT"];
2290 $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL;
2291 }
2292 else
2293 {
2294 if (isset($file["SRC"]))
2295 {
2297 $arCloudImageSizeCache[$file["SRC"]] = [$file["WIDTH"], $file["HEIGHT"]];
2298 }
2299 else
2300 {
2301 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);
2302 }
2303
2304 return [
2305 "src" => $file["SRC"],
2306 "width" => intval($file["WIDTH"]),
2307 "height" => intval($file["HEIGHT"]),
2308 "size" => $file["FILE_SIZE"],
2309 ];
2310 }
2311 }
2312
2314
2315 $cacheImageFile = "/" . $uploadDirName . "/resize_cache/" . $file["SUBDIR"] . "/" . $arSize["width"] . "_" . $arSize["height"] . "_" . $resizeType . (is_array($arFilters) ? md5(serialize($arFilters)) : "") . "/" . $file["FILE_NAME"];
2316 $cacheImageFileCheck = $cacheImageFile;
2317
2318 static $cache = [];
2319 $cache_id = $cacheImageFileCheck;
2320 if (isset($cache[$cache_id]))
2321 {
2322 return $cache[$cache_id];
2323 }
2324 elseif (!file_exists($io->GetPhysicalName($_SERVER["DOCUMENT_ROOT"] . $cacheImageFileCheck)))
2325 {
2326 if (!is_array($arFilters))
2327 {
2328 $arFilters = [
2329 ["name" => "sharpen", "precision" => 15],
2330 ];
2331 }
2332
2333 $sourceImageFile = $_SERVER["DOCUMENT_ROOT"] . $imageFile;
2334 $cacheImageFileTmp = $_SERVER["DOCUMENT_ROOT"] . $cacheImageFile;
2335 $bNeedResize = true;
2336 $callbackData = null;
2337
2338 foreach (GetModuleEvents("main", "OnBeforeResizeImage", true) as $arEvent)
2339 {
2340 if (ExecuteModuleEventEx($arEvent, [
2341 $file,
2342 [$arSize, $resizeType, [], false, $arFilters, $bImmediate],
2343 &$callbackData,
2344 &$bNeedResize,
2345 &$sourceImageFile,
2346 &$cacheImageFileTmp,
2347 ]))
2348 {
2349 break;
2350 }
2351 }
2352
2353 if ($bNeedResize && static::ResizeImageFile($sourceImageFile, $cacheImageFileTmp, $arSize, $resizeType, [], $jpgQuality, $arFilters))
2354 {
2355 $cacheImageFile = mb_substr($cacheImageFileTmp, mb_strlen($_SERVER["DOCUMENT_ROOT"]));
2356
2357 /****************************** QUOTA ******************************/
2358 if (COption::GetOptionInt("main", "disk_space") > 0)
2359 {
2360 CDiskQuota::updateDiskQuota("file", filesize($io->GetPhysicalName($cacheImageFileTmp)), "insert");
2361 }
2362 /****************************** QUOTA ******************************/
2363 }
2364 else
2365 {
2366 $cacheImageFile = $imageFile;
2367 }
2368
2369 foreach (GetModuleEvents("main", "OnAfterResizeImage", true) as $arEvent)
2370 {
2371 if (ExecuteModuleEventEx($arEvent, [
2372 $file,
2373 [$arSize, $resizeType, [], false, $arFilters],
2374 &$callbackData,
2375 &$cacheImageFile,
2376 &$cacheImageFileTmp,
2377 &$arImageSize,
2378 ]))
2379 {
2380 break;
2381 }
2382 }
2383
2384 $cacheImageFileCheck = $cacheImageFile;
2385 }
2386 elseif (defined("BX_FILE_USE_FLOCK"))
2387 {
2388 $hLock = $io->OpenFile($_SERVER["DOCUMENT_ROOT"] . $imageFile, "r+");
2389 if ($hLock)
2390 {
2391 flock($hLock, LOCK_EX);
2392 flock($hLock, LOCK_UN);
2393 fclose($hLock);
2394 }
2395 }
2396
2397 if ($bInitSizes && !is_array($arImageSize))
2398 {
2399 $imageInfo = (new File\Image($_SERVER["DOCUMENT_ROOT"] . $cacheImageFileCheck))->getInfo();
2400 if ($imageInfo)
2401 {
2402 $arImageSize[0] = $imageInfo->getWidth();
2403 $arImageSize[1] = $imageInfo->getHeight();
2404 }
2405 else
2406 {
2407 $arImageSize = [0, 0];
2408 }
2409
2410 $f = $io->GetFile($_SERVER["DOCUMENT_ROOT"] . $cacheImageFileCheck);
2411 $arImageSize[2] = $f->GetFileSize();
2412 }
2413
2414 if (!is_array($arImageSize))
2415 {
2416 $arImageSize = [0, 0, 0];
2417 }
2418
2419 $cache[$cache_id] = [
2420 "src" => $cacheImageFileCheck,
2421 "width" => intval($arImageSize[0]),
2422 "height" => intval($arImageSize[1]),
2423 "size" => $arImageSize[2],
2424 ];
2425 return $cache[$cache_id];
2426 }
2427
2428 public static function ResizeImageDelete($arImage)
2429 {
2431 $upload_dir = COption::GetOptionString("main", "upload_dir", "upload");
2432 $disk_space = COption::GetOptionInt("main", "disk_space");
2433 $delete_size = 0;
2434
2435 $d = $io->GetDirectory($_SERVER["DOCUMENT_ROOT"] . "/" . $upload_dir . "/resize_cache/" . $arImage["SUBDIR"]);
2436
2438 foreach ($d->GetChildren() as $dir_entry)
2439 {
2440 if ($dir_entry->IsDirectory())
2441 {
2442 $f = $io->GetFile($dir_entry->GetPathWithName() . "/" . $arImage["FILE_NAME"]);
2443 if ($f->IsExists())
2444 {
2445 if ($disk_space > 0)
2446 {
2447 $fileSizeTmp = $f->GetFileSize();
2448 if ($io->Delete($f->GetPathWithName()))
2449 {
2450 $delete_size += $fileSizeTmp;
2451 }
2452 }
2453 else
2454 {
2455 $io->Delete($f->GetPathWithName());
2456 }
2457 }
2458
2459 try
2460 {
2461 @rmdir($io->GetPhysicalName($dir_entry->GetPathWithName()));
2462 }
2463 catch (\ErrorException)
2464 {
2465 // Ignore a E_WARNING Error
2466 }
2467 }
2468 }
2469
2470 try
2471 {
2472 @rmdir($io->GetPhysicalName($d->GetPathWithName()));
2473 }
2474 catch (\ErrorException)
2475 {
2476 // Ignore a E_WARNING Error
2477 }
2478
2479 return $delete_size;
2480 }
2481
2487 public static function ImageCreateFromBMP($filename)
2488 {
2489 return imagecreatefrombmp($filename);
2490 }
2491
2502 public static function ScaleImage($sourceImageWidth, $sourceImageHeight, $arSize, $resizeType, &$bNeedCreatePicture, &$arSourceSize, &$arDestinationSize)
2503 {
2504 $source = new Rectangle($sourceImageWidth, $sourceImageHeight);
2505 $destination = new Rectangle($arSize["width"], $arSize["height"]);
2506
2507 $bNeedCreatePicture = $source->resize($destination, $resizeType);
2508
2509 $arSourceSize = [
2510 "x" => $source->getX(),
2511 "y" => $source->getY(),
2512 "width" => $source->getWidth(),
2513 "height" => $source->getHeight(),
2514 ];
2515 $arDestinationSize = [
2516 "x" => $destination->getX(),
2517 "y" => $destination->getY(),
2518 "width" => $destination->getWidth(),
2519 "height" => $destination->getHeight(),
2520 ];
2521 }
2522
2527 public static function IsGD2()
2528 {
2529 return true;
2530 }
2531
2532 public static function ResizeImageFile($sourceFile, $destinationFile, $arSize, $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL, $arWaterMark = [], $quality = false, $arFilters = false)
2533 {
2535
2536 if (!$io->FileExists($sourceFile))
2537 {
2538 return false;
2539 }
2540
2541 if ($resizeType !== BX_RESIZE_IMAGE_EXACT && $resizeType !== BX_RESIZE_IMAGE_PROPORTIONAL_ALT)
2542 {
2543 $resizeType = BX_RESIZE_IMAGE_PROPORTIONAL;
2544 }
2545
2546 if (!is_array($arSize))
2547 {
2548 $arSize = [];
2549 }
2550 if (!array_key_exists("width", $arSize) || intval($arSize["width"]) <= 0)
2551 {
2552 $arSize["width"] = 0;
2553 }
2554 if (!array_key_exists("height", $arSize) || intval($arSize["height"]) <= 0)
2555 {
2556 $arSize["height"] = 0;
2557 }
2558 $arSize["width"] = intval($arSize["width"]);
2559 $arSize["height"] = intval($arSize["height"]);
2560
2561 $sourceImage = new File\Image($io->GetPhysicalName($sourceFile));
2562 $sourceInfo = $sourceImage->getInfo();
2563
2564 if ($sourceInfo === null || !$sourceInfo->isSupported())
2565 {
2566 return false;
2567 }
2568
2569 $fileType = $sourceInfo->getFormat();
2570
2571 $orientation = 0;
2572 if ($fileType == File\Image::FORMAT_JPEG)
2573 {
2574 $exifData = $sourceImage->getExifData();
2575 if (isset($exifData['Orientation']))
2576 {
2577 $orientation = $exifData['Orientation'];
2578 //swap width and height
2579 if ($orientation >= 5 && $orientation <= 8)
2580 {
2581 $sourceInfo->swapSides();
2582 }
2583 }
2584 }
2585
2586 $result = false;
2587
2588 $sourceRectangle = $sourceInfo->toRectangle();
2589 $destinationRectangle = new Rectangle($arSize["width"], $arSize["height"]);
2590
2591 $needResize = $sourceRectangle->resize($destinationRectangle, $resizeType);
2592
2593 $hLock = $io->OpenFile($sourceFile, "r+");
2594 $useLock = defined("BX_FILE_USE_FLOCK");
2595
2596 if ($hLock)
2597 {
2598 if ($useLock)
2599 {
2600 flock($hLock, LOCK_EX);
2601 }
2602 if ($io->FileExists($destinationFile))
2603 {
2604 $destinationInfo = (new File\Image($io->GetPhysicalName($destinationFile)))->getInfo();
2605 if ($destinationInfo)
2606 {
2607 if ($destinationInfo->getWidth() == $destinationRectangle->getWidth() && $destinationInfo->getHeight() == $destinationRectangle->getHeight())
2608 {
2609 //nothing to do
2610 $result = true;
2611 }
2612 }
2613 }
2614 }
2615
2616 if ($result === false)
2617 {
2618 if ($io->Copy($sourceFile, $destinationFile))
2619 {
2620 $destinationImage = new File\Image($io->GetPhysicalName($destinationFile));
2621
2622 if ($destinationImage->load())
2623 {
2624 if ($orientation > 1)
2625 {
2626 $destinationImage->autoRotate($orientation);
2627 }
2628
2629 $modified = false;
2630 if ($needResize)
2631 {
2632 // actual sizes
2633 $sourceRectangle = $destinationImage->getDimensions();
2634 $destinationRectangle = new Rectangle($arSize["width"], $arSize["height"]);
2635
2636 $sourceRectangle->resize($destinationRectangle, $resizeType);
2637
2638 $modified = $destinationImage->resize($sourceRectangle, $destinationRectangle);
2639 }
2640
2641 if (!is_array($arFilters))
2642 {
2643 $arFilters = [];
2644 }
2645
2646 if (is_array($arWaterMark))
2647 {
2648 $arWaterMark["name"] = "watermark";
2649 $arFilters[] = $arWaterMark;
2650 }
2651
2652 foreach ($arFilters as $arFilter)
2653 {
2654 if ($arFilter["name"] == "sharpen" && $arFilter["precision"] > 0)
2655 {
2656 $modified |= $destinationImage->filter(File\Image\Mask::createSharpen($arFilter["precision"]));
2657 }
2658 elseif ($arFilter["name"] == "watermark")
2659 {
2661 $modified |= $destinationImage->drawWatermark($watermark);
2662 }
2663 }
2664
2665 if ($modified)
2666 {
2667 if ($quality === false)
2668 {
2669 $quality = COption::GetOptionString('main', 'image_resize_quality');
2670 }
2671
2672 $io->Delete($destinationFile);
2673
2674 if ($fileType == File\Image::FORMAT_BMP)
2675 {
2676 $destinationImage->saveAs($io->GetPhysicalName($destinationFile), $quality, File\Image::FORMAT_JPEG);
2677 }
2678 else
2679 {
2680 $destinationImage->save($quality);
2681 }
2682
2683 $destinationImage->clear();
2684 }
2685 }
2686
2687 $result = true;
2688 }
2689 }
2690
2691 if ($hLock)
2692 {
2693 if ($useLock)
2694 {
2695 flock($hLock, LOCK_UN);
2696 }
2697 fclose($hLock);
2698 }
2699
2700 return $result;
2701 }
2702
2709 public static function ApplyImageFilter($picture, $arFilter)
2710 {
2711 //prevents destroing outside the function
2712 static $engine;
2713
2714 switch ($arFilter["name"])
2715 {
2716 case "sharpen":
2717 $precision = intval($arFilter["precision"]);
2718 if ($precision > 0)
2719 {
2720 $engine = new File\Image\Gd();
2721 $engine->setResource($picture);
2723 }
2724 return false;
2725 case "watermark":
2726 return static::WaterMark($picture, $arFilter);
2727 }
2728 return false;
2729 }
2730
2735 public static function ImageFlipHorizontal($picture)
2736 {
2737 //prevents destroing outside the function
2738 static $engine;
2739
2740 $engine = new File\Image\Gd();
2741 $engine->setResource($picture);
2742 $engine->flipHorizontal();
2743 }
2744
2751 public static function ImageHandleOrientation($orientation, $sourceImage)
2752 {
2753 if ($orientation <= 1)
2754 {
2755 return false;
2756 }
2757
2758 if (!is_resource($sourceImage))
2759 {
2760 //file
2761 $image = new File\Image($sourceImage);
2762 if ($image->load())
2763 {
2764 if ($image->autoRotate($orientation))
2765 {
2766 $quality = COption::GetOptionString('main', 'image_resize_quality');
2767 $image->save($quality);
2768 }
2769 }
2770 return false;
2771 }
2772
2773 //prevents destroing outside the function
2774 static $engine;
2775
2776 //compatibility around GD image resource
2777 $engine = new File\Image\Gd();
2778 $engine->setResource($sourceImage);
2779
2780 $image = new File\Image();
2781 $image->setEngine($engine);
2782 $image->autoRotate($orientation);
2783
2784 return $engine->getResource();
2785 }
2786
2792 public static function ViewByUser($arFile, $arOptions = [])
2793 {
2794 $previewManager = new Viewer\PreviewManager();
2795 if ($previewManager->isInternalRequest($arFile, $arOptions))
2796 {
2797 $previewManager->processViewByUserRequest($arFile, $arOptions);
2798 }
2799
2801 global $APPLICATION;
2802
2803 $fastDownload = (COption::GetOptionString('main', 'bx_fast_download', 'N') == 'Y');
2804
2805 $attachment_name = "";
2806 $content_type = "";
2807 $specialchars = false;
2808 $force_download = false;
2809 $cache_time = 10800;
2810 $fromClouds = false;
2811 $filename = '';
2812 $fromTemp = false;
2813
2814 if (is_array($arOptions))
2815 {
2816 if (isset($arOptions["content_type"]))
2817 {
2818 $content_type = $arOptions["content_type"];
2819 }
2820 if (isset($arOptions["specialchars"]))
2821 {
2822 $specialchars = $arOptions["specialchars"];
2823 }
2824 if (isset($arOptions["force_download"]))
2825 {
2826 $force_download = $arOptions["force_download"];
2827 }
2828 if (isset($arOptions["cache_time"]))
2829 {
2830 $cache_time = intval($arOptions["cache_time"]);
2831 }
2832 if (isset($arOptions["attachment_name"]))
2833 {
2834 $attachment_name = $arOptions["attachment_name"];
2835 }
2836 if (isset($arOptions["fast_download"]))
2837 {
2838 $fastDownload = (bool)$arOptions["fast_download"];
2839 }
2840 }
2841
2842 if ($cache_time < 0)
2843 {
2844 $cache_time = 0;
2845 }
2846
2847 if (is_array($arFile))
2848 {
2849 if (isset($arFile["SRC"]))
2850 {
2851 $filename = $arFile["SRC"];
2852 }
2853 elseif (isset($arFile["tmp_name"]))
2854 {
2855 if (mb_strpos($arFile['tmp_name'], $_SERVER['DOCUMENT_ROOT']) === 0)
2856 {
2857 $filename = '/' . ltrim(mb_substr($arFile['tmp_name'], mb_strlen($_SERVER['DOCUMENT_ROOT'])), '/');
2858 }
2859 elseif (defined('BX_TEMPORARY_FILES_DIRECTORY') && mb_strpos($arFile['tmp_name'], BX_TEMPORARY_FILES_DIRECTORY) === 0)
2860 {
2861 $fromTemp = true;
2862 $tmpPath = COption::GetOptionString('main', 'bx_tmp_download', '/bx_tmp_download/');
2863 $filename = $tmpPath . ltrim(mb_substr($arFile['tmp_name'], mb_strlen(BX_TEMPORARY_FILES_DIRECTORY)), '/'); //nonexistent path
2864 }
2865 }
2866 else
2867 {
2868 $filename = static::GetFileSRC($arFile);
2869 }
2870 }
2871 elseif (($arFile = static::GetFileArray($arFile)))
2872 {
2873 $filename = $arFile['SRC'];
2874 }
2875
2876 if ($filename == '')
2877 {
2878 return false;
2879 }
2880
2881 if ($content_type == '' && isset($arFile["CONTENT_TYPE"]))
2882 {
2883 $content_type = $arFile["CONTENT_TYPE"];
2884 }
2885
2886 //we produce resized jpg for original bmp
2887 if ($content_type == '' || $content_type == "image/bmp")
2888 {
2889 if (isset($arFile["tmp_name"]))
2890 {
2891 $content_type = static::GetContentType($arFile["tmp_name"], true);
2892 }
2893 else
2894 {
2895 $content_type = static::GetContentType($_SERVER["DOCUMENT_ROOT"] . $filename);
2896 }
2897 }
2898
2899 if (isset($arFile["ORIGINAL_NAME"]) && $arFile["ORIGINAL_NAME"] != '')
2900 {
2901 $name = $arFile["ORIGINAL_NAME"];
2902 }
2903 elseif ($arFile["name"] <> '')
2904 {
2905 $name = $arFile["name"];
2906 }
2907 else
2908 {
2909 $name = $arFile["FILE_NAME"];
2910 }
2911 if (isset($arFile["EXTENSION_SUFFIX"]) && $arFile["EXTENSION_SUFFIX"] <> '')
2912 {
2913 $name = mb_substr($name, 0, -mb_strlen($arFile["EXTENSION_SUFFIX"]));
2914 }
2915
2916 $name = str_replace(["\n", "\r"], '', $name);
2917
2918 if ($attachment_name)
2919 {
2920 $attachment_name = str_replace(["\n", "\r"], '', $attachment_name);
2921 }
2922 else
2923 {
2924 $attachment_name = $name;
2925 }
2926
2927 if (!$force_download)
2928 {
2929 if (!static::IsImage($name, $content_type) || $arFile["HEIGHT"] <= 0 || $arFile["WIDTH"] <= 0)
2930 {
2931 //only valid images can be downloaded inline
2932 $force_download = true;
2933 }
2934 }
2935
2936 $content_type = Web\MimeType::normalize($content_type);
2937
2938 if ($force_download)
2939 {
2940 $specialchars = false;
2941 }
2942
2943 $src = null;
2944 $file = null;
2945
2946 if ((str_starts_with($filename, '/')) && !$fromTemp)
2947 {
2948 $file = new IO\File($_SERVER['DOCUMENT_ROOT'] . $filename);
2949 }
2950 elseif (isset($arFile['tmp_name']))
2951 {
2952 $file = new IO\File($arFile['tmp_name']);
2953 }
2954
2955 if ((str_starts_with($filename, '/')) && ($file instanceof IO\File))
2956 {
2957 try
2958 {
2959 $src = $file->open(IO\FileStreamOpenMode::READ);
2960 }
2961 catch (IO\IoException)
2962 {
2963 return false;
2964 }
2965 }
2966 else
2967 {
2968 if (!$fastDownload)
2969 {
2970 $src = new Web\HttpClient();
2971 }
2972 elseif (intval($arFile['HANDLER_ID']) > 0)
2973 {
2974 $fromClouds = true;
2975 }
2976 }
2977
2978 $APPLICATION->RestartBuffer();
2979 $APPLICATION->EndBufferContentMan();
2980
2981 $cur_pos = 0;
2982 $filesize = (isset($arFile["FILE_SIZE"]) && (int)$arFile["FILE_SIZE"] > 0 ? (int)$arFile["FILE_SIZE"] : (int)($arFile["size"] ?? 0));
2983 $size = $filesize - 1;
2984 $p = strpos($_SERVER["HTTP_RANGE"] ?? '', "=");
2985 if (intval($p) > 0)
2986 {
2987 $bytes = substr($_SERVER["HTTP_RANGE"], $p + 1);
2988 $p = strpos($bytes, "-");
2989 if ($p !== false)
2990 {
2991 $cur_pos = (float)substr($bytes, 0, $p);
2992 $size = (float)substr($bytes, $p + 1);
2993 if ($size <= 0)
2994 {
2995 $size = $filesize - 1;
2996 }
2997 if ($cur_pos > $size)
2998 {
2999 $cur_pos = 0;
3000 $size = $filesize - 1;
3001 }
3002 }
3003 }
3004
3005 if ($file instanceof IO\File)
3006 {
3007 $filetime = $file->getModificationTime();
3008 }
3009 elseif (isset($arFile["tmp_name"]) && $arFile["tmp_name"] <> '')
3010 {
3011 $tmpFile = new IO\File($arFile["tmp_name"]);
3012 $filetime = $tmpFile->getModificationTime();
3013 }
3014 else
3015 {
3016 $filetime = intval(MakeTimeStamp($arFile["TIMESTAMP_X"]));
3017 }
3018
3019 $response = \Bitrix\Main\Context::getCurrent()->getResponse();
3020
3021 if ($_SERVER["REQUEST_METHOD"] == "HEAD")
3022 {
3023 $response->setStatus("200 OK")
3024 ->addHeader("Accept-Ranges", "bytes")
3025 ->addHeader("Content-Type", $content_type)
3026 ->addHeader("Content-Length", ($size - $cur_pos + 1))
3027 ;
3028
3029 if ($filetime > 0)
3030 {
3031 $response->addHeader("Last-Modified", date("r", $filetime));
3032 }
3033 }
3034 else
3035 {
3036 $lastModified = '';
3037 if ($cache_time > 0)
3038 {
3039 //Handle ETag
3040 $ETag = md5($filename . $filesize . $filetime);
3041 if (array_key_exists("HTTP_IF_NONE_MATCH", $_SERVER) && ($_SERVER['HTTP_IF_NONE_MATCH'] === $ETag))
3042 {
3043 $response->setStatus("304 Not Modified");
3044 $response->addHeader("Cache-Control", "private, max-age=" . $cache_time . ", pre-check=" . $cache_time);
3045
3046 $response->writeHeaders();
3047 self::terminate();
3048 }
3049
3050 $response->addHeader("ETag", $ETag);
3051
3052 //Handle Last Modified
3053 if ($filetime > 0)
3054 {
3055 $lastModified = gmdate('D, d M Y H:i:s', $filetime) . ' GMT';
3056 if (array_key_exists("HTTP_IF_MODIFIED_SINCE", $_SERVER) && ($_SERVER['HTTP_IF_MODIFIED_SINCE'] === $lastModified))
3057 {
3058 $response->setStatus("304 Not Modified");
3059 $response->addHeader("Cache-Control", "private, max-age=" . $cache_time . ", pre-check=" . $cache_time);
3060
3061 $response->writeHeaders();
3062 self::terminate();
3063 }
3064 }
3065 }
3066
3067 $utfName = Uri::urnEncode($attachment_name);
3068 $translitName = CUtil::translit($attachment_name, LANGUAGE_ID, [
3069 "max_len" => 1024,
3070 "safe_chars" => ".",
3071 "replace_space" => '-',
3072 "change_case" => false,
3073 ]);
3074
3075 if ($force_download)
3076 {
3077 //Disable zlib for old versions of php <= 5.3.0
3078 //it has broken Content-Length handling
3079 if (ini_get('zlib.output_compression'))
3080 {
3081 ini_set('zlib.output_compression', 'Off');
3082 }
3083
3084 // $p shows that we are sending partial content (range request)
3085 if ($p)
3086 {
3087 $response->setStatus("206 Partial Content");
3088 }
3089 else
3090 {
3091 $response->SetStatus("200 OK");
3092 }
3093
3094 $response->addHeader("Content-Type", $content_type)
3095 ->addHeader("Content-Disposition", "attachment; filename=\"" . $translitName . "\"; filename*=utf-8''" . $utfName)
3096 ->addHeader("Content-Transfer-Encoding", "binary")
3097 ->addHeader("Content-Length", ($size - $cur_pos + 1))
3098 ;
3099
3100 if (is_resource($src))
3101 {
3102 $response->addHeader("Accept-Ranges", "bytes");
3103 $response->addHeader("Content-Range", "bytes " . $cur_pos . "-" . $size . "/" . $filesize);
3104 }
3105 }
3106 else
3107 {
3108 $response->addHeader("Content-Type", $content_type);
3109 $response->addHeader("Content-Disposition", "inline; filename=\"" . $translitName . "\"; filename*=utf-8''" . $utfName);
3110 }
3111
3112 if ($cache_time > 0)
3113 {
3114 $response->addHeader("Cache-Control", "private, max-age=" . $cache_time . ", pre-check=" . $cache_time);
3115 if ($filetime > 0)
3116 {
3117 $response->addHeader('Last-Modified', $lastModified);
3118 }
3119 }
3120 else
3121 {
3122 $response->addHeader("Cache-Control", "no-cache, must-revalidate, post-check=0, pre-check=0");
3123 }
3124
3125 $response->addHeader("Expires", "0");
3126 $response->addHeader("Pragma", "public");
3127
3128 $filenameEncoded = Uri::urnEncode($filename);
3129 // Download from front-end
3130 if ($fastDownload)
3131 {
3132 if ($fromClouds)
3133 {
3134 $filenameDisableProto = preg_replace('~^(https?)(\://)~i', '\\1.', $filenameEncoded);
3135 $cloudUploadPath = COption::GetOptionString('main', 'bx_cloud_upload', '/upload/bx_cloud_upload/');
3136 $response->addHeader('X-Accel-Redirect', rawurlencode($cloudUploadPath . $filenameDisableProto));
3137 }
3138 else
3139 {
3140 $response->addHeader('X-Accel-Redirect', $filenameEncoded);
3141 }
3142 $response->writeHeaders();
3143 self::terminate();
3144 }
3145 else
3146 {
3147 session_write_close();
3148 $response->writeHeaders();
3149
3150 if ($specialchars)
3151 {
3153 echo "<", "pre", ">";
3154 if (is_resource($src))
3155 {
3156 while (!feof($src))
3157 {
3158 echo htmlspecialcharsbx(fread($src, 32768));
3159 }
3160 $file->close();
3161 }
3162 else
3163 {
3165 echo htmlspecialcharsbx($src->get($filenameEncoded));
3166 }
3167 echo "<", "/pre", ">";
3168 }
3169 else
3170 {
3171 if (is_resource($src))
3172 {
3174 $file->seek($cur_pos);
3175 while (!feof($src) && ($cur_pos <= $size))
3176 {
3177 $bufsize = 131072; //128K
3178 if ($cur_pos + $bufsize > $size)
3179 {
3180 $bufsize = $size - $cur_pos + 1;
3181 }
3182 $cur_pos += $bufsize;
3183 echo fread($src, $bufsize);
3184 }
3185 $file->close();
3186 }
3187 else
3188 {
3189 $fp = fopen("php://output", "wb");
3191 $src->setOutputStream($fp);
3192 $src->get($filenameEncoded);
3193 }
3194 }
3195 flush();
3196 self::terminate();
3197 }
3198 }
3199 return true;
3200 }
3201
3202 private static function terminate(): void
3203 {
3205 if (function_exists("fastcgi_finish_request"))
3206 {
3207 //php-fpm
3208 fastcgi_finish_request();
3209 }
3210
3211 Main\Application::getInstance()->terminate();
3212 }
3213
3224 public static function Watermark($obj, $Params)
3225 {
3226 if ($Params['type'] == 'text')
3227 {
3228 $result = static::WatermarkText($obj, $Params);
3229 }
3230 else
3231 {
3232 $result = static::WatermarkImage($obj, $Params);
3233 };
3234
3235 return $result;
3236 }
3237
3244 public static function WatermarkText($obj, $Params = [])
3245 {
3246 //prevents destroing outside the function
3247 static $engine;
3248
3249 $engine = new File\Image\Gd();
3250 $engine->setResource($obj);
3251
3252 $watermark = Image\Watermark::createFromArray($Params);
3253
3254 return $engine->drawTextWatermark($watermark);
3255 }
3256
3267 public static function WatermarkImage($obj, $Params = [])
3268 {
3269 //prevents destroing outside the function
3270 static $engine;
3271
3272 $engine = new File\Image\Gd();
3273 $engine->setResource($obj);
3274
3275 $watermark = Image\Watermark::createFromArray($Params);
3276
3277 return $engine->drawImageWatermark($watermark);
3278 }
3279
3286 public static function ImageRotate($sourceFile, $angle)
3287 {
3288 $image = new File\Image($sourceFile);
3289 if (!$image->load())
3290 {
3291 return false;
3292 }
3293
3294 $quality = COption::GetOptionString('main', 'image_resize_quality');
3295
3296 $result = ($image->rotate($angle) && $image->save($quality));
3297
3298 $image->clear();
3299
3300 return $result;
3301 }
3302
3308 public static function CreateImage($path)
3309 {
3310 $image = new File\Image\Gd($path);
3311
3312 if ($image->load())
3313 {
3314 return $image->getResource();
3315 }
3316
3317 return false;
3318 }
3319
3325 public static function ExtractImageExif($src)
3326 {
3327 return (new File\Image($src))->getExifData();
3328 }
3329
3335 public static function NormalizeContentType($contentType)
3336 {
3338 }
3339
3340 public static function GetContentType($path, $bPhysicalName = false)
3341 {
3342 if ($bPhysicalName)
3343 {
3344 $pathX = $path;
3345 }
3346 else
3347 {
3349 $pathX = $io->GetPhysicalName($path);
3350 }
3351
3352 $type = "";
3353 if (function_exists("mime_content_type"))
3354 {
3355 $type = mime_content_type($pathX);
3356 }
3357
3358 if ($type == "" && function_exists("image_type_to_mime_type"))
3359 {
3360 $info = (new File\Image($pathX))->getInfo();
3361 if ($info)
3362 {
3363 $type = $info->getMime();
3364 }
3365 }
3366
3367 if ($type == "")
3368 {
3369 $type = Web\MimeType::getByFileExtension(substr($pathX, bxstrrpos($pathX, ".") + 1));
3370 }
3371
3372 return $type;
3373 }
3374
3382 public static function GetImageSize($path, $bPhysicalName = false, $flashEnabled = false)
3383 {
3384 if (!$bPhysicalName)
3385 {
3387 $path = $io->GetPhysicalName($path);
3388 }
3389
3390 $image = new File\Image($path);
3391
3392 if (($info = $image->getInfo($flashEnabled)) !== null)
3393 {
3394 return [
3395 0 => $info->getWidth(),
3396 1 => $info->getHeight(),
3397 2 => $info->getFormat(),
3398 3 => $info->getAttributes(),
3399 "mime" => $info->getMime(),
3400 ];
3401 }
3402 return false;
3403 }
3404
3405 public static function DeleteHashAgent()
3406 {
3407 global $DB;
3408
3409 $res = $DB->Query("select distinct h1.FILE_ID
3410 FROM b_file_hash h1, b_file_hash h2
3411 WHERE h1.FILE_ID > h2.FILE_ID
3412 AND h1.FILE_SIZE = h2.FILE_SIZE
3413 AND h1.FILE_HASH = h2.FILE_HASH
3414 limit 10000
3415 ");
3416
3417 $delete = [];
3418 while ($ar = $res->Fetch())
3419 {
3420 $delete[] = $ar['FILE_ID'];
3421 }
3422
3423 if (!empty($delete))
3424 {
3425 $DB->Query("DELETE FROM b_file_hash WHERE FILE_ID IN (" . implode(',', $delete) . ")");
3426
3427 return __METHOD__ . '();';
3428 }
3429
3430 return '';
3431 }
3432}
3433
$path
Определения access_edit.php:21
$connection
Определения actionsdefinitions.php:38
$hash
Определения ajax_redirector.php:8
$type
Определения options.php:106
global $APPLICATION
Определения include.php:80
$module_id
Определения options.php:6
static getInstance()
Определения application.php:98
static getConnection($name="")
Определения application.php:638
Определения Gd.php:14
static createSharpen($precision)
Определения Mask.php:59
static createFromArray($params)
Определения Watermark.php:160
static markDeleted($originalId)
Определения FileDuplicateTable.php:59
Определения file.php:6
static convertPhysicalToLogical($path)
Определения path.php:144
static getList(array $parameters=array())
Определения datamanager.php:431
static delete($primary)
Определения datamanager.php:1644
static add(array $data)
Определения datamanager.php:877
static update($primary, array $data)
Определения datamanager.php:1256
static filter()
Определения query.php:906
static getString($length, $caseSensitive=false)
Определения random.php:76
static isImage($mime)
Определения mimetype.php:278
static normalize($contentType)
Определения mimetype.php:287
static getByFileExtension($extension)
Определения mimetype.php:247
Определения uri.php:17
static urnEncode($str, $charset='UTF-8')
Определения uri.php:416
static GetOptionString($module_id, $name, $def="", $site=false, $bExactSite=false)
Определения option.php:8
static GetInstance()
Определения virtual_io.php:60
Определения dbresult.php:88
Определения quota.php:6
Определения file.php:25
static GetFromDb($fileId, $realId)
Определения file.php:1039
static checkForDb($arFields, $field)
Определения file.php:55
static ImageCreateFromBMP($filename)
Определения file.php:2487
static CloneFile(int $fileId)
Определения file.php:632
static GetList($arOrder=[], $arFilter=[])
Определения file.php:1069
static GetPath($img_id)
Определения file.php:1794
static ShowImage($strImage, $iMaxW=0, $iMaxH=0, $sParams=null, $strImageUrl="", $bPopup=false, $sPopupTitle=false, $iSizeWHTTP=0, $iSizeHHTTP=0, $strImageUrlTemplate="")
Определения file.php:1805
static processVersions($ID)
Определения file.php:889
static UpdateDesc($ID, $desc)
Определения file.php:1354
static GetTempName($dir_name=false, $file_name='')
Определения file.php:2167
static ResizeImageGet($file, $arSize, $resizeType=BX_RESIZE_IMAGE_PROPORTIONAL, $bInitSizes=false, $arFilters=false, $bImmediate=false, $jpgQuality=false)
Определения file.php:2242
static Delete($ID)
Определения file.php:706
const DELETE_NONE
Определения file.php:27
static Watermark($obj, $Params)
Определения file.php:3224
const DELETE_DB
Определения file.php:29
static FindDuplicate($size, $hash, $handlerId=null)
Определения file.php:442
static DeleteDuplicates($originalId, array $duplicteIds)
Определения file.php:521
static CheckFile($arFile, $intMaxSize=0, $mimeType=false, $strExt=false, $bForceMD5=false, $bSkipExt=false)
Определения file.php:1556
const DELETE_ALL
Определения file.php:30
static AddVersion($originalId, $versionId, $metaData=[])
Определения file.php:875
static AddDuplicate($originalId, $duplicateId=null, bool $resolvePossibleOriginCycle=true)
Определения file.php:482
static ImageHandleOrientation($orientation, $sourceImage)
Определения file.php:2751
static ShowFile($iFileID, $max_file_size=0, $iMaxW=0, $iMaxH=0, $bPopup=false, $sParams=false, $sPopupTitle=false, $iSizeWHTTP=0, $iSizeHHTTP=0)
Определения file.php:1639
static OutputJSImgShw()
Определения file.php:1671
static WatermarkImage($obj, $Params=[])
Определения file.php:3267
static DisableJSFunction($b=true)
Определения file.php:1665
static ChangeSubDir($module_id, $old_subdir, $new_subdir)
Определения file.php:2177
static SaveFile($arFile, $strSavePath, $forceRandom=false, $skipExtension=false, $dirAdd='')
Определения file.php:164
static ResizeImageFile($sourceFile, $destinationFile, $arSize, $resizeType=BX_RESIZE_IMAGE_PROPORTIONAL, $arWaterMark=[], $quality=false, $arFilters=false)
Определения file.php:2532
static GetFromCache($fileId, $realId=false)
Определения file.php:936
static ExtractImageExif($src)
Определения file.php:3325
const DELETE_FILE
Определения file.php:28
static GetImageExtensions()
Определения file.php:1467
static GetFileArray($fileId, $uploadDir=false)
Определения file.php:1207
const CACHE_DIR
Определения file.php:26
static processDuplicates($ID)
Определения file.php:796
static IsGD2()
Определения file.php:2527
static _GetImgParams($strImage, $iSizeWHTTP=0, $iSizeHHTTP=0)
Определения file.php:1726
static CopyFile($FILE_ID, $bRegister=true, $newPath="")
Определения file.php:1259
static GetFileSRC($file, $uploadDir=false, $external=true)
Определения file.php:1172
static CheckImageFile($arFile, $iMaxSize=0, $iMaxWidth=0, $iMaxHeight=0, $access_typies=[], $bForceMD5=false, $bSkipExt=false)
Определения file.php:1493
static 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)
Определения file.php:1379
static UpdateExternalId($ID, $external_id)
Определения file.php:1366
static CleanCache($fileId)
Определения file.php:915
static ResizeImage(&$arFile, $arSize, $resizeType=BX_RESIZE_IMAGE_PROPORTIONAL)
Определения file.php:2203
static GetImageSize($path, $bPhysicalName=false, $flashEnabled=false)
Определения file.php:3382
static CreateImage($path)
Определения file.php:3308
static SaveForDB(&$arFields, $field, $strSavePath)
Определения file.php:32
static GetFlashExtensions()
Определения file.php:1472
static transformName($name, $forceRandom=false, $bSkipExt=false)
Определения file.php:75
static ImageRotate($sourceFile, $angle)
Определения file.php:3286
static IsImage($filename, $mime_type=false)
Определения file.php:1477
static GetContentType($path, $bPhysicalName=false)
Определения file.php:3340
static WatermarkText($obj, $Params=[])
Определения file.php:3244
static validateFile($strFileName, $arFile)
Определения file.php:122
static NormalizeContentType($contentType)
Определения file.php:3335
static FormatSize($size, $precision=2)
Определения file.php:1453
static DoInsert($arFields)
Определения file.php:650
static DeleteHashAgent()
Определения file.php:3405
static ScaleImage($sourceImageWidth, $sourceImageHeight, $arSize, $resizeType, &$bNeedCreatePicture, &$arSourceSize, &$arDestinationSize)
Определения file.php:2502
static ApplyImageFilter($picture, $arFilter)
Определения file.php:2709
static ResizeImageDeleteCache($arFile)
Определения file.php:2230
static ImageFlipHorizontal($picture)
Определения file.php:2735
static ConvertFilesToPost($source, &$target, $field=false)
Определения file.php:1226
static CalculateHash($file, $size)
Определения file.php:422
static MakeFileArray($path, $mimetype=false, $skipInternal=false, $external_id="")
Определения file.php:2005
static GetByID($fileId, $realId=false)
Определения file.php:1021
static translit($str, $lang, $params=[])
Определения util.php:629
static addslashes($s)
Определения util.php:10
$content
Определения commerceml.php:144
$f
Определения component_props.php:52
$bytes
Определения cron_html_pages.php:17
$arFields
Определения dblapprove.php:5
$filename
Определения file_edit.php:47
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$zr
Определения options.php:5
$result
Определения get_property_values.php:14
if($ajaxMode) $ID
Определения get_user.php:27
$p
Определения group_list_element_edit.php:23
$cancel
Определения iblock_catalog_edit.php:371
$filter
Определения iblock_catalog_list.php:54
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
global $DB
Определения cron_frame.php:29
$io
Определения csv_new_run.php:98
global $arCloudImageSizeCache
Определения file.php:3434
const BX_RESIZE_IMAGE_EXACT
Определения constants.php:12
const BX_RESIZE_IMAGE_PROPORTIONAL
Определения constants.php:11
const BX_RESIZE_IMAGE_PROPORTIONAL_ALT
Определения constants.php:10
$z
Определения options.php:31
$arOptions
Определения structure.php:223
if($NS['step']==6) if( $NS[ 'step']==7) if(COption::GetOptionInt('main', 'disk_space', 0) > 0) $info
Определения backup.php:924
roundEx($value, $prec=0)
Определения tools.php:4635
GetFileExtension($path)
Определения tools.php:2972
CheckDirPath($path)
Определения tools.php:2707
ExecuteModuleEventEx($arEvent, $arParams=[])
Определения tools.php:5214
IsFileUnsafe($name)
Определения tools.php:3016
bxstrrpos($haystack, $needle)
Определения tools.php:3292
RemoveScriptExtension($check_name)
Определения tools.php:2939
bx_basename($path, $ext="")
Определения tools.php:3269
GetDirPath($sPath)
Определения tools.php:3245
RewriteFile($abs_path, $strContent)
Определения tools.php:2895
GetFileType($path)
Определения tools.php:3047
htmlspecialcharsEx($str)
Определения tools.php:2685
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
GetModuleEvents($MODULE_ID, $MESSAGE_ID, $bReturnArray=false)
Определения tools.php:5177
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
is_set($a, $k=false)
Определения tools.php:2133
CopyDirFiles($path_from, $path_to, $ReWrite=true, $Recursive=false, $bDeleteAfterCopy=false, $strExclude="")
Определения tools.php:2732
GetMessage($name, $aReplace=null)
Определения tools.php:3397
GetFileNameWithoutExtension($path)
Определения tools.php:2986
GetFileName($path)
Определения tools.php:3001
MakeTimeStamp($datetime, $format=false)
Определения tools.php:538
$SHOWIMAGEFIRST
Определения tools.php:5143
$name
Определения menu_edit.php:35
Определения arrayresult.php:2
Определения Color.php:9
Определения Image.php:9
Определения directory.php:3
Определения chain.php:3
Определения cookie.php:3
$files
Определения mysql_to_pgsql.php:30
if(mb_strlen($order)< 6) $desc
Определения payment.php:44
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$ar
Определения options.php:199
$fileName
Определения quickway.php:305
if(empty($signedUserToken)) $key
Определения quickway.php:257
$contentType
Определения quickway.php:301
$precision
Определения template.php:403
else $a
Определения template.php:137
$val
Определения options.php:1793
$response
Определения result.php:21
$engine
Определения options.php:121
$error
Определения subscription_card_product.php:20
$rs
Определения action.php:82
$arFilter
Определения user_search.php:106