1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
zip.php
См. документацию.
1<?php
8
10
11class CZip implements IBXArchive
12{
13 const ReadBlockSize = 2048;
14
15 public $zipname = '';
16 public $zipfile = 0;
17 private CBXVirtualIo $io;
18 private $arErrors = [];
19 private $fileSystemEncoding;
20 private $startFile;
21 private $arHeaders;
22
23 //should be changed via SetOptions
24 private $compress = true;
25 private $remove_path = "";
26 private $add_path = "";
27 private $replaceExistentFiles = false;
28 private $checkPermissions = true;
29 private $rule = [];
30 private $step_time = 30;
31 private $arPackedFiles = [];
32 private $arPackedFilesData = [];
33
34 public function __construct($pzipname)
35 {
36 $this->io = CBXVirtualIo::GetInstance();
37 $this->zipname = $this->_convertWinPath($pzipname, false);
38 $this->_errorReset();
39 $this->fileSystemEncoding = $this->_getfileSystemEncoding();
40 }
41
48 public function Pack($arFileList, $startFile = "")
49 {
50 $this->_errorReset();
51 $this->startFile = $this->io->GetPhysicalName($startFile);
52 $this->arPackedFiles = [];
53 $this->arHeaders = [];
54 $arCentralDirInfo = [];
55 $zipfile_tmp = $zipname_tmp = '';
56
57 $isNewArchive = true;
58 if ($startFile != "" && is_file($this->io->GetPhysicalName($this->zipname)))
59 {
60 $isNewArchive = false;
61 }
62
63 if ($isNewArchive)
64 {
65 if (!$this->_openFile("wb"))
66 {
67 return false;
68 }
69 }
70 else
71 {
72 if (!$this->_openFile("rb"))
73 {
74 return false;
75 }
76
77 // read the central directory
78 if (($res = $this->_readEndCentralDir($arCentralDirInfo)) != 1)
79 {
80 $this->_closeFile();
81 return $res;
82 }
83
84 @rewind($this->zipfile);
85
86 //creating tmp file
87 $zipname_tmp = GetDirPath($this->zipname) . uniqid('ziparc') . '.tmp';
88 if (($zipfile_tmp = @fopen($this->io->GetPhysicalName($zipname_tmp), 'wb')) == 0)
89 {
90 $this->_closeFile();
91 $this->_errorLog("ERR_READ_TMP", str_replace("#FILE_NAME#", removeDocRoot($zipname_tmp), GetMessage("MAIN_ZIP_ERR_READ_TMP")));
92 return $this->arErrors;
93 }
94
95 //copy files from the archive to the tmp file
96 $size = $arCentralDirInfo['offset'];
97
98 while ($size != 0)
99 {
100 $length = ($size < self::ReadBlockSize ? $size : self::ReadBlockSize);
101 $buffer = fread($this->zipfile, $length);
102 @fwrite($zipfile_tmp, $buffer, $length);
103 $size -= $length;
104 }
105
106 //swapping file handle to use methods on the temporary file, not the real archive
107 $tmp_id = $this->zipfile;
108 $this->zipfile = $zipfile_tmp;
109 $zipfile_tmp = $tmp_id;
110 }
111
112 //starting measuring start time from here (only packing time)
113 define("ZIP_START_TIME", microtime(true));
114 $this->tempres = null;
115
116 $arFileList = $this->_parseFileParams($arFileList);
117
118 $arConvertedFileList = [];
119 foreach ($arFileList as $fullpath)
120 {
121 $arConvertedFileList[] = $this->io->GetPhysicalName($fullpath);
122 }
123
124 $packRes = null;
125 if (is_array($arFileList) && !empty($arFileList))
126 {
127 $packRes = $this->_processFiles($arConvertedFileList, $this->add_path, $this->remove_path);
128 }
129
130 if ($isNewArchive)
131 {
132 //writing Central Directory
133 //save central directory offset
134 $offset = @ftell($this->zipfile);
135
136 //make central dir files header
137 for ($i = 0, $counter = 0; $i < sizeof($this->arPackedFiles); $i++)
138 {
139 //write file header
140 if ($this->arHeaders[$i]['status'] == 'ok')
141 {
142 if (($res = $this->_writeCentralFileHeader($this->arHeaders[$i])) != 1)
143 {
144 return $res;
145 }
146 $counter++;
147 }
148
149 $this->_convertHeader2FileInfo($this->arHeaders[$i], $this->arPackedFilesData[$i]);
150 }
151
152 $zip_comment = '';
153 //calculate the size of the central header
154 $size = @ftell($this->zipfile) - $offset;
155 //make central dir footer
156 if (($res = $this->_writeCentralHeader($counter, $size, $offset, $zip_comment)) != 1)
157 {
158 $this->arHeaders = null;
159 return $res;
160 }
161 }
162 else
163 {
164 //save the offset of the central dir
165 $offset = @ftell($this->zipfile);
166
167 //copy file headers block from the old archive
168 $size = $arCentralDirInfo['size'];
169 while ($size != 0)
170 {
171 $length = ($size < self::ReadBlockSize ? $size : self::ReadBlockSize);
172 $buffer = @fread($zipfile_tmp, $length);
173 @fwrite($this->zipfile, $buffer, $length);
174 $size -= $length;
175 }
176
177 //add central dir files header
178 for ($i = 0, $counter = 0; $i < sizeof($this->arHeaders); $i++)
179 {
180 //create the file header
181 if ($this->arHeaders[$i]['status'] == 'ok')
182 {
183 if (($res = $this->_writeCentralFileHeader($this->arHeaders[$i])) != 1)
184 {
185 fclose($zipfile_tmp);
186 $this->_closeFile();
187 @unlink($this->io->GetPhysicalName($zipname_tmp));
188
189 return $res;
190 }
191 $counter++;
192 }
193 //convert header to the usable format
194 $this->_convertHeader2FileInfo($this->arHeaders[$i], $this->arPackedFilesData[$i]);
195 }
196
197 $zip_comment = '';
198
199 //find the central header size
200 $size = @ftell($this->zipfile) - $offset;
201
202 //make central directory footer
203 if (($res = $this->_writeCentralHeader($counter + $arCentralDirInfo['entries'], $size, $offset, $zip_comment)) != 1)
204 {
205 //clear file list
206 $this->arHeaders = null;
207 return $res;
208 }
209
210 //changing file handler back
211 $tmp_id = $this->zipfile;
212 $this->zipfile = $zipfile_tmp;
213 $zipfile_tmp = $tmp_id;
214
215 $this->_closeFile();
216 @fclose($zipfile_tmp);
217 // @unlink($this->zipname);
218
219 //probably test the result @rename($zipname_tmp, $this->zipname);
220 $this->_renameTmpFile($zipname_tmp, $this->zipname);
221 }
222
223 if ($isNewArchive && ($res === false))
224 {
225 $this->_cleanFile();
226 }
227 else
228 {
229 $this->_closeFile();
230 }
231
232 //if packing is not completed, remember last file
233 if ($packRes === 'continue')
234 {
235 $this->startFile = $this->io->GetLogicalName(array_pop($this->arPackedFiles));
236 }
237
238 if ($packRes === false)
239 {
241 }
242 elseif ($packRes && $this->startFile == "")
243 {
245 }
246 elseif ($packRes && $this->startFile != "")
247 {
248 //call Pack() with $this->GetStartFile() next time to continue
250 }
251 return null;
252 }
253
254 private function _haveTime()
255 {
256 return microtime(true) - ZIP_START_TIME < $this->step_time;
257 }
258
259 private function _processFiles($arFileList, $addPath, $removePath)
260 {
261 $addPath = str_replace("\\", "/", $addPath);
262 $removePath = str_replace("\\", "/", $removePath);
263
264 if (!$this->zipfile)
265 {
266 $this->arErrors[] = ["ERR_DFILE", GetMessage("MAIN_ZIP_ERR_DFILE")];
267 return false;
268 }
269
270 if (!is_array($arFileList) || empty($arFileList))
271 {
272 return true;
273 }
274
275 $j = -1;
276
277 if (!isset($this->tempres))
278 {
279 $this->tempres = "started";
280 }
281
282 //files and directory scan
283 while ($j++ < count($arFileList) && ($this->tempres === "started"))
284 {
285 $filename = $arFileList[$j] ?? '';
286
287 if ($filename == '')
288 {
289 continue;
290 }
291
292 if (!file_exists($filename))
293 {
294 $this->arErrors[] = ["ERR_NO_FILE", str_replace("#FILE_NAME#", $filename, GetMessage("MAIN_ZIP_ERR_NO_FILE"))];
295 continue;
296 }
297
298 //is a file
299 if (!@is_dir($filename))
300 {
301 $filename = str_replace("//", "/", $filename);
302
303 //jumping to startFile, if it's specified
304 if ($this->startFile <> '')
305 {
306 if ($filename != $this->startFile)
307 {
308 //don't pack - jump to the next file
309 continue;
310 }
311 else
312 {
313 //if startFile is found, continue to pack files and folders without startFile, starting from next
314 $this->startFile = null;
315 continue;
316 }
317 }
318
319 //check product permissions
320 if ($this->checkPermissions)
321 {
323 {
324 continue;
325 }
326 }
327
328 if ($this->_haveTime())
329 {
330 if (!$this->_addFile($filename, $arFileHeaders, $this->add_path, $this->remove_path))
331 {
332 //$arErrors is filled in the _addFile method
333 $this->tempres = false;
334 }
335 else
336 {
337 //remember last file
338 $this->arPackedFiles[] = $filename;
339 $this->arHeaders[] = $arFileHeaders;
340 }
341 }
342 else
343 {
344 $this->tempres = 'continue';
345 return $this->tempres;
346 }
347 }
348 //if directory
349 else
350 {
351 if (!($handle = opendir($filename)))
352 {
353 $this->arErrors[] = ["ERR_DIR_OPEN_FAIL", str_replace("#DIR_NAME#", $filename, GetMessage("MAIN_ZIP_ERR_DIR_OPEN_FAIL"))];
354 continue;
355 }
356
357 if ($this->checkPermissions)
358 {
359 if (!CBXArchive::HasAccess($filename, false))
360 {
361 continue;
362 }
363 }
364
365 while (false !== ($dir = readdir($handle)))
366 {
367 if ($dir != "." && $dir != "..")
368 {
369 $arFileList_tmp = [];
370 if ($filename != ".")
371 {
372 $arFileList_tmp[] = $filename . '/' . $dir;
373 }
374 else
375 {
376 $arFileList_tmp[] = $dir;
377 }
378
379 $this->_processFiles($arFileList_tmp, $addPath, $removePath);
380 }
381 }
382
383 unset($arFileList_tmp);
384 unset($dir);
385 unset($handle);
386 }
387 }
388
389 return $this->tempres;
390 }
391
396 public function GetStartFile()
397 {
398 return $this->startFile;
399 }
400
406 public function Unpack($strPath)
407 {
408 $this->SetOptions(["ADD_PATH" => $strPath]);
409
410 $rule = $this->GetOptions()['RULE'];
411
412 $arParams = [
413 "add_path" => $this->add_path,
414 "remove_path" => $this->remove_path,
415 "extract_as_string" => false,
416 "remove_all_path" => false,
417 "callback_pre_extract" => "",
418 "callback_post_extract" => "",
419 "set_chmod" => 0,
420 "by_name" => "",
421 "by_index" => array_key_exists("by_index", $rule) ? $rule['by_index'] : "",
422 "by_preg" => "",
423 ];
424
425 @set_time_limit(0);
426 $result = $this->Extract($arParams);
427
428 if ($result === 0)
429 {
430 return false;
431 }
432 //if there was no error, but we didn't extract any file ($this->replaceExistentFile = false)
433 else
434 {
435 if ($result == [])
436 {
437 return true;
438 }
439 else
440 {
441 return $result;
442 }
443 }
444 }
445
451 public function SetOptions($arOptions)
452 {
453 if (array_key_exists("COMPRESS", $arOptions))
454 {
455 $this->compress = $arOptions["COMPRESS"] === true;
456 }
457
458 if (array_key_exists("ADD_PATH", $arOptions))
459 {
460 $this->add_path = $this->io->GetPhysicalName(str_replace("\\", "/", strval($arOptions["ADD_PATH"])));
461 }
462
463 if (array_key_exists("REMOVE_PATH", $arOptions))
464 {
465 $this->remove_path = $this->io->GetPhysicalName(str_replace("\\", "/", strval($arOptions["REMOVE_PATH"])));
466 }
467
468 if (array_key_exists("STEP_TIME", $arOptions))
469 {
470 $this->step_time = floatval($arOptions["STEP_TIME"]);
471 }
472
473 if (array_key_exists("UNPACK_REPLACE", $arOptions))
474 {
475 $this->replaceExistentFiles = $arOptions["UNPACK_REPLACE"] === true;
476 }
477
478 if (array_key_exists("CHECK_PERMISSIONS", $arOptions))
479 {
480 $this->checkPermissions = $arOptions["CHECK_PERMISSIONS"] === true;
481 }
482
483 if (array_key_exists("RULE", $arOptions))
484 {
485 $this->rule = $arOptions["RULE"];
486 }
487 }
488
493 public function GetOptions()
494 {
495 $arOptions = [
496 "COMPRESS" => $this->compress,
497 "ADD_PATH" => $this->add_path,
498 "REMOVE_PATH" => $this->remove_path,
499 "STEP_TIME" => $this->step_time,
500 "UNPACK_REPLACE" => $this->replaceExistentFiles,
501 "CHECK_PERMISSIONS" => $this->checkPermissions,
502 "RULE" => $this->rule,
503 ];
504
505 return $arOptions;
506 }
507
512 public function GetErrors()
513 {
514 return $this->arErrors;
515 }
516
523 public function Create($arFileList, $arParams = 0)
524 {
525 $this->_errorReset();
526
527 if ($arParams === 0)
528 {
529 $arParams = [];
530 }
531
532 if ($this->_checkParams($arParams,
533 ['no_compression' => false,
534 'add_path' => "",
535 'remove_path' => "",
536 'remove_all_path' => false]) != 1)
537 {
538 return 0;
539 }
540
541 $arResultList = [];
542 if (is_array($arFileList))
543 {
544 $res = $this->_createArchive($arFileList, $arResultList, $arParams);
545 }
546 else
547 {
548 if (is_string($arFileList))
549 {
550 $arTmpList = explode(",", $arFileList);
551 $res = $this->_createArchive($arTmpList, $arResultList, $arParams);
552 }
553 else
554 {
555 $this->_errorLog("ERR_PARAM", GetMessage("MAIN_ZIP_ERR_PARAM"));
556 $res = "ERR_PARAM";
557 }
558 }
559
560 if ($res != 1)
561 {
562 return 0;
563 }
564
565 return $arResultList;
566 }
567
574 public function Add($arFileList, $arParams = 0)
575 {
576 $this->_errorReset();
577
578 if ($arParams === 0)
579 {
580 $arParams = [];
581 }
582
583 if ($this->_checkParams($arParams,
584 ['no_compression' => false,
585 'add_path' => '',
586 'remove_path' => '',
587 'remove_all_path' => false,
588 'callback_pre_add' => '',
589 'callback_post_add' => '']) != 1)
590 {
591 return 0;
592 }
593
594 $arResultList = [];
595 if (is_array($arFileList))
596 {
597 $res = $this->_addData($arFileList, $arResultList, $arParams);
598 }
599 else
600 {
601 if (is_string($arFileList))
602 {
603 $arTmpList = explode(",", $arFileList);
604 $res = $this->_addData($arTmpList, $arResultList, $arParams);
605 }
606 else
607 {
608 $this->_errorLog("ERR_PARAM_LIST", GetMessage("MAIN_ZIP_ERR_PARAM_LIST"));
609 $res = "ERR_PARAM_LIST";
610 }
611 }
612
613 if ($res != 1)
614 {
615 return 0;
616 }
617
618 return $arResultList;
619 }
620
625 public function GetContent()
626 {
627 $this->_errorReset();
628
629 if (!$this->_checkFormat())
630 {
631 return (0);
632 }
633
634 $arTmpList = [];
635 if ($this->_getFileList($arTmpList) != 1)
636 {
637 unset($arTmpList);
638 return (0);
639 }
640
641 return $arTmpList;
642 }
643
649 public function Extract($arParams = 0)
650 {
651 $this->_errorReset();
652
653 if (!$this->_checkFormat())
654 {
655 return (0);
656 }
657
658 if ($arParams === 0)
659 {
660 $arParams = [];
661 }
662
663 if ($this->_checkParams($arParams,
664 ['extract_as_string' => false,
665 'add_path' => '',
666 'remove_path' => '',
667 'remove_all_path' => false,
668 'callback_pre_extract' => '',
669 'callback_post_extract' => '',
670 'set_chmod' => 0,
671 'by_name' => '',
672 'by_index' => '',
673 'by_preg' => '']) != 1)
674 {
675 return 0;
676 }
677
678 $arTmpList = [];
679 if ($this->_extractByRule($arTmpList, $arParams) != 1)
680 {
681 unset($arTmpList);
682 return (0);
683 }
684
685 return $arTmpList;
686 }
687
693 public function Delete($arParams)
694 {
695 $this->_errorReset();
696
697 if (!$this->_checkFormat())
698 {
699 return (0);
700 }
701
702 if ($this->_checkParams($arParams, ['by_name' => '', 'by_index' => '', 'by_preg' => '']) != 1)
703 {
704 return 0;
705 }
706
707 //at least one rule should be set
708 if (($arParams['by_name'] == '') && ($arParams['by_index'] == '') && ($arParams['by_preg'] == ''))
709 {
710 $this->_errorLog("ERR_PARAM_RULE", GetMessage("MAIN_ZIP_ERR_PARAM_RULE"));
711 return 0;
712 }
713
714 $arTmpList = [];
715 if ($this->_deleteByRule($arTmpList, $arParams) != 1)
716 {
717 unset($arTmpList);
718 return (0);
719 }
720
721 return $arTmpList;
722 }
723
728 public function GetProperties()
729 {
730 $this->_errorReset();
731
732 if (!$this->_checkFormat())
733 {
734 return (0);
735 }
736
737 $arProperties = [];
738 $arProperties['comment'] = '';
739 $arProperties['nb'] = 0;
740 $arProperties['status'] = 'not_exist';
741
742 if (@is_file($this->io->GetPhysicalName($this->zipname)))
743 {
744 if (($this->zipfile = @fopen($this->io->GetPhysicalName($this->zipname), 'rb')) == 0)
745 {
746 $this->_errorLog("ERR_READ", str_replace("#FILE_NAME#", removeDocRoot($this->zipname), GetMessage("MAIN_ZIP_ERR_READ")));
747 return 0;
748 }
749
750 //read central directory info
751 $arCentralDirInfo = [];
752 if (($this->_readEndCentralDir($arCentralDirInfo)) != 1)
753 {
754 return 0;
755 }
756
757 $this->_closeFile();
758
759 //set user attributes
760 $arProperties['comment'] = $arCentralDirInfo['comment'];
761 $arProperties['nb'] = $arCentralDirInfo['entries'];
762 $arProperties['status'] = 'ok';
763 }
764
765 return $arProperties;
766 }
767
768 private function _checkFormat()
769 {
770 $this->_errorReset();
771
772 if (!is_file($this->io->GetPhysicalName($this->zipname)))
773 {
774 $this->_errorLog("ERR_MISSING_FILE", str_replace("#FILE_NAME#", removeDocRoot($this->zipname), GetMessage("MAIN_ZIP_ERR_MISSING_FILE")));
775 return (false);
776 }
777
778 if (!is_readable($this->io->GetPhysicalName($this->zipname)))
779 {
780 $this->_errorLog("ERR_READ", str_replace("#FILE_NAME#", removeDocRoot($this->zipname), GetMessage("MAIN_ZIP_ERR_READ")));
781 return (false);
782 }
783
784 //possible checks: magic code, central header, each file header
785 return true;
786 }
787
788 private function _createArchive($arFilesList, &$arResultList, &$arParams)
789 {
790 $addDir = $arParams['add_path'];
791 $removeDir = $arParams['remove_path'];
792 $removeAllDir = $arParams['remove_all_path'];
793
794 if (($res = $this->_openFile('wb')) != 1)
795 {
796 return $res;
797 }
798
799 $res = $this->_addList($arFilesList, $arResultList, $addDir, $removeDir, $removeAllDir, $arParams);
800
801 $this->_closeFile();
802
803 return $res;
804 }
805
806 private function _addData($arFilesList, &$arResultList, &$arParams)
807 {
808 $addDir = $arParams['add_path'];
809 $removeDir = $arParams['remove_path'];
810 $removeAllDir = $arParams['remove_all_path'];
811
812 if ((!is_file($this->io->GetPhysicalName($this->zipname))) || (filesize($this->io->GetPhysicalName($this->zipname)) == 0))
813 {
814 $res = $this->_createArchive($arFilesList, $arResultList, $arParams);
815 return $res;
816 }
817
818 if (($res = $this->_openFile('rb')) != 1)
819 {
820 return $res;
821 }
822
823 $arCentralDirInfo = [];
824 if (($res = $this->_readEndCentralDir($arCentralDirInfo)) != 1)
825 {
826 $this->_closeFile();
827 return $res;
828 }
829
830 @rewind($this->zipfile);
831
832 $zipname_tmp = GetDirPath($this->zipname) . uniqid('ziparc') . '.tmp';
833
834 if (($zipfile_tmp = @fopen($this->io->GetPhysicalName($zipname_tmp), 'wb')) == 0)
835 {
836 $this->_closeFile();
837 $this->_errorLog("ERR_READ_TMP", str_replace("#FILE_NAME#", removeDocRoot($zipname_tmp), GetMessage("MAIN_ZIP_ERR_READ_TMP")));
838 return $this->arErrors;
839 }
840
841 //copy files from archive to the tmp file
842 $size = $arCentralDirInfo['offset'];
843 while ($size != 0)
844 {
845 $length = ($size < self::ReadBlockSize ? $size : self::ReadBlockSize);
846
847 $buffer = fread($this->zipfile, $length);
848
849 @fwrite($zipfile_tmp, $buffer, $length);
850 $size -= $length;
851 }
852
853 //changing file handles to use methods on the temporary file, not the real archive
854 $tmp_id = $this->zipfile;
855 $this->zipfile = $zipfile_tmp;
856 $zipfile_tmp = $tmp_id;
857
858 $arHeaders = [];
859 if (($res = $this->_addFileList($arFilesList, $arHeaders,
860 $addDir, $removeDir,
861 $removeAllDir, $arParams)) != 1)
862 {
863 fclose($zipfile_tmp);
864 $this->_closeFile();
865 @unlink($this->io->GetPhysicalName($zipname_tmp));
866
867 return $res;
868 }
869
870 //save central dir offset
871 $offset = @ftell($this->zipfile);
872
873 //copy file headers block from the old archive
874 $size = $arCentralDirInfo['size'];
875 while ($size != 0)
876 {
877 $length = ($size < self::ReadBlockSize ? $size : self::ReadBlockSize);
878 $buffer = @fread($zipfile_tmp, $length);
879 @fwrite($this->zipfile, $buffer, $length);
880 $size -= $length;
881 }
882
883 //write central dir files header
884 for ($i = 0, $counter = 0; $i < sizeof($arHeaders); $i++)
885 {
886 //add the file header
887 if ($arHeaders[$i]['status'] == 'ok')
888 {
889 if (($res = $this->_writeCentralFileHeader($arHeaders[$i])) != 1)
890 {
891 fclose($zipfile_tmp);
892 $this->_closeFile();
893 @unlink($this->io->GetPhysicalName($zipname_tmp));
894 return $res;
895 }
896 $counter++;
897 }
898
899 $this->_convertHeader2FileInfo($arHeaders[$i], $arResultList[$i]);
900 }
901
902 $zip_comment = '';
903
904 //size of the central header
905 $size = @ftell($this->zipfile) - $offset;
906
907 //make central dir footer
908 if (($res = $this->_writeCentralHeader($counter + $arCentralDirInfo['entries'], $size, $offset, $zip_comment)) != 1)
909 {
910 //reset files list
911 unset($arHeaders);
912 return $res;
913 }
914
915 //change back file handler
916 $tmp_id = $this->zipfile;
917 $this->zipfile = $zipfile_tmp;
918 $zipfile_tmp = $tmp_id;
919
920 $this->_closeFile();
921 @fclose($zipfile_tmp);
922 @unlink($this->io->GetPhysicalName($this->zipname));
923 //possibly test the result @rename($zipname_tmp, $this->zipname);
924 $this->_renameTmpFile($zipname_tmp, $this->zipname);
925
926 return $res;
927 }
928
929 private function _openFile($mode)
930 {
931 $res = 1;
932
933 if ($this->zipfile != 0)
934 {
935 $this->_errorLog("ERR_OPEN", str_replace("#FILE_NAME#", removeDocRoot($this->zipname), GetMessage("MAIN_ZIP_ERR_READ_OPEN")));
936 return $this->arErrors;
937 }
938
939 $this->_checkDirPath($this->zipname);
940
941 if (($this->zipfile = @fopen($this->io->GetPhysicalName($this->zipname), $mode)) == 0)
942 {
943 $this->_errorLog("ERR_READ_MODE", str_replace(["#FILE_NAME#", "#MODE#"], [removeDocRoot($this->zipname), $mode], GetMessage("MAIN_ZIP_ERR_READ_MODE")));
944 return $this->arErrors;
945 }
946
947 return $res;
948 }
949
950 private function _closeFile()
951 {
952 if ($this->zipfile != 0)
953 {
954 @fclose($this->zipfile);
955 }
956 $this->zipfile = 0;
957 }
958
959 private function _addList($arFilesList, &$arResultList, $addDir, $removeDir, $removeAllDir, &$arParams)
960 {
961 $arHeaders = [];
962 if (($res = $this->_addFileList($arFilesList, $arHeaders, $addDir, $removeDir, $removeAllDir, $arParams)) != 1)
963 {
964 return $res;
965 }
966
967 //save the offset of the central dir
968 $offset = @ftell($this->zipfile);
969
970 //make central dir files header
971 for ($i = 0, $counter = 0; $i < sizeof($arHeaders); $i++)
972 {
973 if ($arHeaders[$i]['status'] == 'ok')
974 {
975 if (($res = $this->_writeCentralFileHeader($arHeaders[$i])) != 1)
976 {
977 return $res;
978 }
979 $counter++;
980 }
981 $this->_convertHeader2FileInfo($arHeaders[$i], $arResultList[$i]);
982 }
983
984 $zip_comment = '';
985
986 //the size of the central header
987 $size = @ftell($this->zipfile) - $offset;
988
989 //add central dir footer
990 if (($res = $this->_writeCentralHeader($counter, $size, $offset, $zip_comment)) != 1)
991 {
992 unset($arHeaders);
993 return $res;
994 }
995 return $res;
996 }
997
998 private function _addFileList($arFilesList, &$arResultList, $addDir, $removeDir, $removeAllDir, &$arParams)
999 {
1000 $res = 1;
1001 $header = [];
1002
1003 //save the current number of elements in the result list
1004 $count = sizeof($arResultList);
1005 $filesListCount = count($arFilesList);
1006 for ($j = 0; ($j < $filesListCount) && ($res == 1); $j++)
1007 {
1008 $filename = $this->_convertWinPath($arFilesList[$j], false);
1009
1010 //if empty - skip
1011 if ($filename == "")
1012 {
1013 continue;
1014 }
1015
1016 if (!file_exists($this->io->GetPhysicalName($filename)))
1017 {
1018 $this->_errorLog("ERR_MISSING_FILE", str_replace("#FILE_NAME#", removeDocRoot($filename), GetMessage("MAIN_ZIP_ERR_MISSING_FILE")));
1019 return $this->arErrors;
1020 }
1021
1022 if ((is_file($this->io->GetPhysicalName($filename))) || ((is_dir($this->io->GetPhysicalName($filename))) && !$removeAllDir))
1023 {
1024 if (($res = $this->_addFile($filename, $header, $addDir, $removeDir, $removeAllDir, $arParams)) != 1)
1025 {
1026 return $res;
1027 }
1028
1029 //save file info
1030 $arResultList[$count++] = $header;
1031 }
1032
1033 if (is_dir($this->io->GetPhysicalName($filename)))
1034 {
1035 if ($filename != ".")
1036 {
1037 $path = $filename . "/";
1038 }
1039 else
1040 {
1041 $path = "";
1042 }
1043
1044 //read the folder for files and subfolders
1045 $hdir = opendir($this->io->GetPhysicalName($filename));
1046
1047 while ($hitem = readdir($hdir))
1048 {
1049 if ($hitem == '.' || $hitem == '..')
1050 {
1051 continue;
1052 }
1053
1054 if (is_file($this->io->GetPhysicalName($path . $hitem)))
1055 {
1056 if (($res = $this->_addFile($path . $hitem, $header, $addDir, $removeDir, $removeAllDir, $arParams)) != 1)
1057 {
1058 return $res;
1059 }
1060 //save file info
1061 $arResultList[$count++] = $header;
1062 }
1063 else
1064 {
1065 //should be ana array as a parameter
1066 $arTmpList[0] = $path . $hitem;
1067
1068 $res = $this->_addFileList($arTmpList, $arResultList, $addDir, $removeDir, $removeAllDir, $arParams);
1069 $count = sizeof($arResultList);
1070 }
1071 }
1072
1073 //unset variables for the recursive call
1074 unset($arTmpList);
1075 unset($hdir);
1076 unset($hitem);
1077 }
1078 }
1079
1080 return $res;
1081 }
1082
1083 private function _addFile($filename, &$arHeader, $addDir, $removeDir, $removeAllDir = false, $arParams = [])
1084 {
1085 $res = 1;
1086
1087 if ($filename == "")
1088 {
1089 $this->_errorLog("ERR_PARAM_LIST", GetMessage("MAIN_ZIP_ERR_PARAM_LIST"));
1090 return $this->arErrors;
1091 }
1092
1093 //saved filename
1094 $storedFilename = $filename;
1095
1096 //remove the path
1097 if ($removeAllDir)
1098 {
1099 $storedFilename = basename($filename);
1100 }
1101 else
1102 {
1103 if ($removeDir != "")
1104 {
1105 if (!str_ends_with($removeDir, '/'))
1106 {
1107 $removeDir .= "/";
1108 }
1109
1110 if ((str_starts_with($filename, "./")) || (str_starts_with($removeDir, "./")))
1111 {
1112 if ((str_starts_with($filename, "./")) && (!str_starts_with($removeDir, "./")))
1113 {
1114 $removeDir = "./" . $removeDir;
1115 }
1116 if ((!str_starts_with($filename, "./")) && (str_starts_with($removeDir, "./")))
1117 {
1118 $removeDir = substr($removeDir, 2);
1119 }
1120 }
1121
1122 $incl = $this->_containsPath($removeDir, $filename);
1123
1124 if ($incl > 0)
1125 {
1126 if ($incl == 2)
1127 {
1128 $storedFilename = "";
1129 }
1130 else
1131 {
1132 $storedFilename = mb_substr($filename, mb_strlen($removeDir));
1133 }
1134 }
1135 }
1136 }
1137
1138 if ($addDir != "")
1139 {
1140 if (str_ends_with($addDir, "/"))
1141 {
1142 $storedFilename = $addDir . $storedFilename;
1143 }
1144 else
1145 {
1146 $storedFilename = $addDir . "/" . $storedFilename;
1147 }
1148 }
1149
1150 //make the filename
1151 $storedFilename = $this->_reducePath($storedFilename);
1152
1153 //save file properties
1154 clearstatcache();
1155 $arHeader['comment'] = '';
1156 $arHeader['comment_len'] = 0;
1157 $arHeader['compressed_size'] = 0;
1158 $arHeader['compression'] = 0;
1159 $arHeader['crc'] = 0;
1160 $arHeader['disk'] = 0;
1161 $arHeader['external'] = (is_file($filename) ? 0xFE49FFE0 : 0x41FF0010);
1162 $arHeader['extra'] = '';
1163 $arHeader['extra_len'] = 0;
1164 $arHeader['filename'] = \Bitrix\Main\Text\Encoding::convertEncoding($filename, $this->fileSystemEncoding, "cp866");
1165 $arHeader['filename_len'] = strlen(\Bitrix\Main\Text\Encoding::convertEncoding($filename, $this->fileSystemEncoding, "cp866"));
1166 $arHeader['flag'] = 0;
1167 $arHeader['index'] = -1;
1168 $arHeader['internal'] = 0;
1169 $arHeader['mtime'] = filemtime($filename);
1170 $arHeader['offset'] = 0;
1171 $arHeader['size'] = filesize($filename);
1172 $arHeader['status'] = 'ok';
1173 $arHeader['stored_filename'] = \Bitrix\Main\Text\Encoding::convertEncoding($storedFilename, $this->fileSystemEncoding, "cp866");
1174 $arHeader['version'] = 20;
1175 $arHeader['version_extracted'] = 10;
1176
1177 //pre-add callback
1178 if ((isset($arParams['callback_pre_add'])) && ($arParams['callback_pre_add'] != ''))
1179 {
1180 //generate local information
1181 $arLocalHeader = [];
1182 $this->_convertHeader2FileInfo($arHeader, $arLocalHeader);
1183
1184 //callback call
1185 eval('$res = ' . $arParams['callback_pre_add'] . '(\'callback_pre_add\', $arLocalHeader);');
1186 //if res == 0 change the file status
1187 if ($res == 0)
1188 {
1189 $arHeader['status'] = "skipped";
1190 $res = 1;
1191 }
1192
1193 //update the info, only some fields can be modified
1194 if ($arHeader['stored_filename'] != $arLocalHeader['stored_filename'])
1195 {
1196 $arHeader['stored_filename'] = $this->_reducePath($arLocalHeader['stored_filename']);
1197 }
1198 }
1199
1200 //if stored filename is empty - filter
1201 if ($arHeader['stored_filename'] == "")
1202 {
1203 $arHeader['status'] = "filtered";
1204 }
1205
1206 //check path length
1207 if (mb_strlen($arHeader['stored_filename']) > 0xFF)
1208 {
1209 $arHeader['status'] = 'filename_too_long';
1210 }
1211
1212 //if no error
1213 if ($arHeader['status'] == 'ok')
1214 {
1215 if (is_file($filename))
1216 {
1217 //reading source
1218 if (($file = @fopen($filename, "rb")) == 0)
1219 {
1220 $this->_errorLog("ERR_READ", str_replace("#FILE_NAME#", removeDocRoot($filename), GetMessage("MAIN_ZIP_ERR_READ")));
1221 return $this->arErrors;
1222 }
1223
1224 //reading the file content
1225 $content = $arHeader['size'] > 0 ? fread($file, $arHeader['size']) : '';
1226 //calculating crc
1227 $arHeader['crc'] = crc32($content);
1228 //compress the file
1229 $compressedContent = empty($arParams['no_compression']) ? gzdeflate($content) : $content;
1230
1231 //set header params
1232 $arHeader['compressed_size'] = strlen($compressedContent);
1233 $arHeader['compression'] = 8;
1234
1235 //generate header
1236 if (($res = $this->_writeFileHeader($arHeader)) != 1)
1237 {
1238 @fclose($file);
1239 return $res;
1240 }
1241
1242 //writing the compressed content
1243 $binary_data = pack('a' . $arHeader['compressed_size'], $compressedContent);
1244 @fwrite($this->zipfile, $binary_data, $arHeader['compressed_size']);
1245
1246 @fclose($file);
1247 }
1248 //if directory
1249 else
1250 {
1251 //set file properties
1252 $arHeader['filename'] .= '/';
1253 $arHeader['filename_len']++;
1254 $arHeader['size'] = 0;
1255 //folder value. to be checked
1256 $arHeader['external'] = 0x41FF0010;
1257
1258 //generate header
1259 if (($res = $this->_writeFileHeader($arHeader)) != 1)
1260 {
1261 return $res;
1262 }
1263 }
1264 }
1265
1266 //pre-add callack
1267 if ((isset($arParams['callback_post_add'])) && ($arParams['callback_post_add'] != ''))
1268 {
1269 //make local info
1270 $arLocalHeader = [];
1271 $this->_convertHeader2FileInfo($arHeader, $arLocalHeader);
1272
1273 //callback call
1274 eval('$res = ' . $arParams['callback_post_add'] . '(\'callback_post_add\', $arLocalHeader);');
1275
1276 if ($res == 0)
1277 {
1278 $res = 1;
1279 } //ignored
1280 }
1281
1282 return $res;
1283 }
1284
1285 private function _writeFileHeader(&$arHeader)
1286 {
1287 $res = 1;
1288
1289 //to be checked: for(reset($arHeader); $key = key($arHeader); next($arHeader))
1290
1291 //save offset position of the file
1292 $arHeader['offset'] = ftell($this->zipfile);
1293
1294 //transform unix modification time to the dos mdate/mtime format
1295 $date = getdate($arHeader['mtime']);
1296 $mtime = ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2;
1297 $mdate = (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday'];
1298
1299 // $arHeader["stored_filename"] = "12345678.gif";
1300
1301 //pack data
1302 $binary_data = pack("VvvvvvVVVvv",
1303 0x04034b50,
1304 $arHeader['version'],
1305 $arHeader['flag'],
1306 $arHeader['compression'],
1307 $mtime,
1308 $mdate,
1309 $arHeader['crc'],
1310 $arHeader['compressed_size'],
1311 $arHeader['size'],
1312 strlen($arHeader['stored_filename']),
1313 $arHeader['extra_len']
1314 );
1315
1316 //write first 148 bytes of the header in the archive
1317 fputs($this->zipfile, $binary_data, 30);
1318
1319 //write the variable fields
1320 if ($arHeader['stored_filename'] <> '')
1321 {
1322 fputs($this->zipfile, $arHeader['stored_filename'], strlen($arHeader['stored_filename']));
1323 }
1324 if ($arHeader['extra_len'] != 0)
1325 {
1326 fputs($this->zipfile, $arHeader['extra'], $arHeader['extra_len']);
1327 }
1328
1329 return $res;
1330 }
1331
1332 private function _writeCentralFileHeader($arHeader)
1333 {
1334 $res = 1;
1335
1336 //to be checked: for(reset($arHeader); $key = key($arHeader); next($arHeader)) {}
1337
1338 //convert unix mtime to dos mdate/mtime
1339 $date = getdate($arHeader['mtime']);
1340 $mtime = ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2;
1341 $mdate = (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday'];
1342
1343 //pack data
1344 $binary_data = pack("VvvvvvvVVVvvvvvVV",
1345 0x02014b50,
1346 $arHeader['version'],
1347 $arHeader['version_extracted'],
1348 $arHeader['flag'],
1349 $arHeader['compression'],
1350 $mtime,
1351 $mdate,
1352 $arHeader['crc'],
1353 $arHeader['compressed_size'],
1354 $arHeader['size'],
1355 strlen($arHeader['stored_filename']),
1356 $arHeader['extra_len'],
1357 $arHeader['comment_len'],
1358 $arHeader['disk'],
1359 $arHeader['internal'],
1360 $arHeader['external'],
1361 $arHeader['offset']);
1362
1363 //write 42 bytes of the header in the zip file
1364 fputs($this->zipfile, $binary_data, 46);
1365
1366 //variable fields
1367 if ($arHeader['stored_filename'] <> '')
1368 {
1369 fputs($this->zipfile, $arHeader['stored_filename'], strlen($arHeader['stored_filename']));
1370 }
1371 if ($arHeader['extra_len'] != 0)
1372 {
1373 fputs($this->zipfile, $arHeader['extra'], $arHeader['extra_len']);
1374 }
1375 if ($arHeader['comment_len'] != 0)
1376 {
1377 fputs($this->zipfile, $arHeader['comment'], $arHeader['comment_len']);
1378 }
1379
1380 return $res;
1381 }
1382
1383 private function _writeCentralHeader($entriesNumber, $blockSize, $offset, $comment)
1384 {
1385 $res = 1;
1386
1387 //packed data
1388 $binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $entriesNumber, $entriesNumber, $blockSize, $offset, mb_strlen($comment));
1389
1390 //22 bytes of the header in the zip file
1391 fputs($this->zipfile, $binary_data, 22);
1392
1393 //variable fields
1394 if ($comment <> '')
1395 {
1396 fputs($this->zipfile, $comment, mb_strlen($comment));
1397 }
1398
1399 return $res;
1400 }
1401
1402 private function _getFileList(&$arFilesList)
1403 {
1404 if (($this->zipfile = @fopen($this->io->GetPhysicalName($this->zipname), 'rb')) == 0)
1405 {
1406 $this->_errorLog("ERR_READ", str_replace("#FILE_NAME#", removeDocRoot($this->zipname), GetMessage("MAIN_ZIP_ERR_READ")));
1407 return $this->arErrors;
1408 }
1409
1410 //get central directory information
1411 $arCentralDirInfo = [];
1412 if (($res = $this->_readEndCentralDir($arCentralDirInfo)) != 1)
1413 {
1414 return $res;
1415 }
1416
1417 //go to the beginning of the central directory
1418 @rewind($this->zipfile);
1419
1420 if (@fseek($this->zipfile, $arCentralDirInfo['offset']))
1421 {
1422 $this->_errorLog("ERR_INVALID_ARCHIVE_ZIP", GetMessage("MAIN_ZIP_ERR_INVALID_ARCHIVE_ZIP"));
1423 return $this->arErrors;
1424 }
1425
1426 //read each entry
1427 for ($i = 0; $i < $arCentralDirInfo['entries']; $i++)
1428 {
1429 //read the file header
1430 if (($res = $this->_readCentralFileHeader($header)) != 1)
1431 {
1432 return $res;
1433 }
1434 $header['index'] = $i;
1435
1436 //get only interesting attributes
1437 $this->_convertHeader2FileInfo($header, $arFilesList[$i]);
1438 unset($header);
1439 }
1440
1441 $this->_closeFile();
1442 return $res;
1443 }
1444
1445 private function _convertHeader2FileInfo($arHeader, &$arInfo)
1446 {
1447 $res = 1;
1448 $arInfo = [];
1449
1450 //get necessary attributes
1451 $arInfo['filename'] = $arHeader['filename'];
1452 $arInfo['stored_filename'] = $arHeader['stored_filename'];
1453 $arInfo['size'] = $arHeader['size'];
1454 $arInfo['compressed_size'] = $arHeader['compressed_size'];
1455 $arInfo['mtime'] = $arHeader['mtime'];
1456 $arInfo['comment'] = $arHeader['comment'];
1457 $arInfo['folder'] = (($arHeader['external'] & 0x00000010) == 0x00000010);
1458 $arInfo['index'] = $arHeader['index'];
1459 $arInfo['status'] = $arHeader['status'];
1460
1461 return $res;
1462 }
1463
1464 private function _extractByRule(&$arFileList, &$arParams)
1465 {
1466 $path = $arParams['add_path'];
1467 $removePath = $arParams['remove_path'];
1468 $removeAllPath = $arParams['remove_all_path'];
1469
1470 //path checking
1471 if (($path == "") || ((!str_starts_with($path, "/")) && (!str_starts_with($path, "../")) && (mb_substr($path, 1, 2) != ":/")))
1472 {
1473 $path = "./" . $path;
1474 }
1475
1476 //reduce the path last (and duplicated) '/'
1477 if (($path != "./") && ($path != "/"))
1478 {
1479 // checking path end '/'
1480 while (str_ends_with($path, "/"))
1481 {
1482 $path = substr($path, 0, -1);
1483 }
1484 }
1485
1486 //path should end with the /
1487 if (($removePath != "") && (!str_ends_with($removePath, '/')))
1488 {
1489 $removePath .= '/';
1490 }
1491
1492 if (($res = $this->_openFile('rb')) != 1)
1493 {
1494 return $res;
1495 }
1496
1497 //reading central directory information
1498 $arCentralDirInfo = [];
1499 if (($res = $this->_readEndCentralDir($arCentralDirInfo)) != 1)
1500 {
1501 $this->_closeFile();
1502 return $res;
1503 }
1504
1505 //starting from the beginning of the central directory
1506 $entryPos = $arCentralDirInfo['offset'];
1507
1508 //reading each entry
1509 $j_start = 0;
1510
1511 for ($i = 0, $extractedCounter = 0; $i < $arCentralDirInfo['entries']; $i++)
1512 {
1513 //reading next central directory record
1514 @rewind($this->zipfile);
1515 if (@fseek($this->zipfile, $entryPos))
1516 {
1517 $this->_closeFile();
1518 $this->_errorLog("ERR_INVALID_ARCHIVE_ZIP", GetMessage("MAIN_ZIP_ERR_MISSING_FILE"));
1519 return $this->arErrors;
1520 }
1521
1522 //reading the file header
1523 $header = [];
1524 if (($res = $this->_readCentralFileHeader($header)) != 1)
1525 {
1526 $this->_closeFile();
1527 return $res;
1528 }
1529
1530 //saving the index
1531 $header['index'] = $i;
1532
1533 //saving the file pos
1534 $entryPos = ftell($this->zipfile);
1535
1536 $extract = false;
1537
1538 //look for the specific extract rules
1539 if ((isset($arParams['by_name'])) && is_array($arParams['by_name']))
1540 {
1541 //is filename in the list
1542 $count = count($arParams['by_name']);
1543 for ($j = 0; $j < $count && !$extract; $j++)
1544 {
1545 //is directory
1546 if (str_ends_with($arParams['by_name'][$j], "/"))
1547 {
1548 //is dir in the filename path
1549 if ((mb_strlen($header['stored_filename']) > mb_strlen($arParams['by_name'][$j]))
1550 && (str_starts_with($header['stored_filename'], $arParams['by_name'][$j])))
1551 {
1552 $extract = true;
1553 }
1554 }
1555 else
1556 {
1557 if ($header['stored_filename'] == $arParams['by_name'][$j])
1558 {
1559 $extract = true;
1560 }
1561 }
1562 }
1563 }
1564 else
1565 {
1566 if ((isset($arParams['by_preg'])) && ($arParams['by_preg'] != ""))
1567 {
1568 //extract by preg rule
1569 if (preg_match($arParams['by_preg'], $header['stored_filename']))
1570 {
1571 $extract = true;
1572 }
1573 }
1574 else
1575 {
1576 if ((isset($arParams['by_index'])) && is_array($arParams['by_index']))
1577 {
1578 //extract by index rule (if index is in the list)
1579 for ($j = $j_start, $n = count($arParams['by_index']); $j < $n && !$extract; $j++)
1580 {
1581 if (($i >= $arParams['by_index'][$j]['start']) && ($i <= $arParams['by_index'][$j]['end']))
1582 {
1583 $extract = true;
1584 }
1585
1586 if ($i >= $arParams['by_index'][$j]['end'])
1587 {
1588 $j_start = $j + 1;
1589 }
1590
1591 if ($arParams['by_index'][$j]['start'] > $i)
1592 {
1593 break;
1594 }
1595 }
1596 }
1597 else
1598 {
1599 $extract = true;
1600 }
1601 }
1602 }
1603
1604 // extract file
1605 if ($extract)
1606 {
1607 @rewind($this->zipfile);
1608 if (@fseek($this->zipfile, $header['offset']))
1609 {
1610 $this->_closeFile();
1611 $this->_errorLog("ERR_INVALID_ARCHIVE_ZIP", GetMessage("MAIN_ZIP_ERR_INVALID_ARCHIVE_ZIP"));
1612 return $this->arErrors;
1613 }
1614 //extract as a string
1615 if ($arParams['extract_as_string'])
1616 {
1617 //extract the file
1618 if (($res = $this->_extractFileAsString($header, $string)) != 1)
1619 {
1620 $this->_closeFile();
1621 return $res;
1622 }
1623
1624 //get attributes
1625 if (($res = $this->_convertHeader2FileInfo($header, $arFileList[$extractedCounter])) != 1)
1626 {
1627 $this->_closeFile();
1628 return $res;
1629 }
1630
1631 //set file content
1632 $arFileList[$extractedCounter]['content'] = $string;
1633
1634 //next extracted file
1635 $extractedCounter++;
1636 }
1637 else
1638 {
1639 if (($res = $this->_extractFile($header, $path, $removePath, $removeAllPath, $arParams)) != 1)
1640 {
1641 $this->_closeFile();
1642 return $res;
1643 }
1644
1645 //get attributes
1646 if (($res = $this->_convertHeader2FileInfo($header, $arFileList[$extractedCounter++])) != 1)
1647 {
1648 $this->_closeFile();
1649 return $res;
1650 }
1651 }
1652 }
1653 }
1654 $this->_closeFile();
1655 return $res;
1656 }
1657
1658 private function _extractFile(&$arEntry, $path, $removePath, $removeAllPath, $arParams)
1659 {
1660 if (($res = $this->_readFileHeader($header)) != 1)
1661 {
1662 return $res;
1663 }
1664 //to be checked: file header should be coherent with $arEntry info
1665
1666 $arEntry["filename"] = \Bitrix\Main\Text\Encoding::convertEncoding($arEntry["filename"], "cp866", $this->fileSystemEncoding);
1667 $arEntry["stored_filename"] = \Bitrix\Main\Text\Encoding::convertEncoding($arEntry["stored_filename"], "cp866", $this->fileSystemEncoding);
1668
1669 //protecting against ../ etc. in file path
1670 //only absolute path should be in the $arEntry
1671 $arEntry['filename'] = _normalizePath($arEntry['filename']);
1672 $arEntry['stored_filename'] = _normalizePath($arEntry['stored_filename']);
1673
1674 if ($removeAllPath)
1675 {
1676 $arEntry['filename'] = basename($arEntry['filename']);
1677 }
1678 else
1679 {
1680 if ($removePath != "")
1681 {
1682 if ($this->_containsPath($removePath, $arEntry['filename']) == 2)
1683 {
1684 //change file status
1685 $arEntry['status'] = "filtered";
1686 return $res;
1687 }
1688
1689 if (str_starts_with($arEntry['filename'], $removePath))
1690 {
1691 //remove path
1692 $arEntry['filename'] = substr($arEntry['filename'], strlen($removePath));
1693 }
1694 }
1695 }
1696
1697 //making absolute path to the extracted file out of filename stored in the zip header and passed extracting path
1698 if ($path != '')
1699 {
1700 $arEntry['filename'] = $path . "/" . $arEntry['filename'];
1701 }
1702
1703 //pre-extract callback
1704 if ((isset($arParams['callback_pre_extract']))
1705 && ($arParams['callback_pre_extract'] != ''))
1706 {
1707 //generate local info
1708 $arLocalHeader = [];
1709 $this->_convertHeader2FileInfo($arEntry, $arLocalHeader);
1710
1711 //callback call
1712 eval('$res = ' . $arParams['callback_pre_extract'] . '(\'callback_pre_extract\', $arLocalHeader);');
1713
1714 //change file status
1715 if ($res == 0)
1716 {
1717 $arEntry['status'] = "skipped";
1718 $res = 1;
1719 }
1720
1721 //update the info, only some fields can be modified
1722 $arEntry['filename'] = $arLocalHeader['filename'];
1723 }
1724
1725 //check if extraction should be done
1726 if ($arEntry['status'] == 'ok')
1727 {
1728 if ($this->checkPermissions && !CBXArchive::IsFileSafe($arEntry['filename']))
1729 {
1730 $arEntry['status'] = "no_permissions";
1731 }
1732 else
1733 {
1734 //if the file exists, change status
1735 if (file_exists($arEntry['filename']))
1736 {
1737 if (is_dir($arEntry['filename']))
1738 {
1739 $arEntry['status'] = "already_a_directory";
1740 }
1741 else
1742 {
1743 if (!is_writeable($arEntry['filename']))
1744 {
1745 $arEntry['status'] = "write_protected";
1746 }
1747 else
1748 {
1749 if ((filemtime($arEntry['filename']) > $arEntry['mtime']) && (!$this->replaceExistentFiles))
1750 {
1751 $arEntry['status'] = "newer_exist";
1752 }
1753 }
1754 }
1755 }
1756 else
1757 {
1758 //check the directory availability and create it if necessary
1759 if ((($arEntry['external'] & 0x00000010) == 0x00000010) || (str_ends_with($arEntry['filename'], '/')))
1760 {
1761 $checkDir = $arEntry['filename'];
1762 }
1763 else
1764 {
1765 if (!mb_strstr($arEntry['filename'], "/"))
1766 {
1767 $checkDir = "";
1768 }
1769 else
1770 {
1771 $checkDir = dirname($arEntry['filename']);
1772 }
1773 }
1774
1775 if (($res = $this->_checkDir($checkDir, (($arEntry['external'] & 0x00000010) == 0x00000010))) != 1)
1776 {
1777 //change file status
1778 $arEntry['status'] = "path_creation_fail";
1779
1780 //return $res;
1781 $res = 1;
1782 }
1783 }
1784 }
1785 }
1786
1787 //check if extraction should be done
1788 if ($arEntry['status'] == 'ok')
1789 {
1790 //if not a folder - extract
1791 if (!(($arEntry['external'] & 0x00000010) == 0x00000010))
1792 {
1793 //if zip file with 0 compression
1794 if (($arEntry['compression'] == 0) && ($arEntry['compressed_size'] == $arEntry['size']))
1795 {
1796 if (($destFile = @fopen($arEntry['filename'], 'wb')) == 0)
1797 {
1798 $arEntry['status'] = "write_error";
1799 return $res;
1800 }
1801
1802 //reading the fileby by self::ReadBlockSize octets blocks
1803 $size = $arEntry['compressed_size'];
1804 while ($size != 0)
1805 {
1806 $length = ($size < self::ReadBlockSize ? $size : self::ReadBlockSize);
1807 $buffer = fread($this->zipfile, $length);
1808 $binary_data = pack('a' . $length, $buffer);
1809 @fwrite($destFile, $binary_data, $length);
1810 $size -= $length;
1811 }
1812
1813 //close the destination file
1814 fclose($destFile);
1815
1816 //changing file modification time
1817 touch($arEntry['filename'], $arEntry['mtime']);
1818 }
1819 else
1820 {
1821 if (($destFile = @fopen($arEntry['filename'], 'wb')) == 0)
1822 {
1823 //change file status
1824 $arEntry['status'] = "write_error";
1825 return $res;
1826 }
1827
1828 //read the compressed file in a buffer (one shot)
1829 $buffer = @fread($this->zipfile, $arEntry['compressed_size']);
1830
1831 //decompress the file
1832 $fileContent = gzinflate($buffer);
1833 unset($buffer);
1834
1835 //write uncompressed data
1836 @fwrite($destFile, $fileContent, $arEntry['size']);
1837 unset($fileContent);
1838 @fclose($destFile);
1839 touch($arEntry['filename'], $arEntry['mtime']);
1840 }
1841
1842 if ((isset($arParams['set_chmod'])) && ($arParams['set_chmod'] != 0))
1843 {
1844 chmod($arEntry['filename'], $arParams['set_chmod']);
1845 }
1846 }
1847 }
1848
1849 //post-extract callback
1850 if ((isset($arParams['callback_post_extract'])) && ($arParams['callback_post_extract'] != ''))
1851 {
1852 //make local info
1853 $arLocalHeader = [];
1854 $this->_convertHeader2FileInfo($arEntry, $arLocalHeader);
1855
1856 //callback call
1857 eval('$res = ' . $arParams['callback_post_extract'] . '(\'callback_post_extract\', $arLocalHeader);');
1858 }
1859 return $res;
1860 }
1861
1862 private function _extractFileAsString($arEntry, &$string)
1863 {
1864 //reading file header
1865 $header = [];
1866 if (($res = $this->_readFileHeader($header)) != 1)
1867 {
1868 return $res;
1869 }
1870
1871 //to be checked: file header should be coherent with the $arEntry info
1872
1873 //extract if not a folder
1874 if (!(($arEntry['external'] & 0x00000010) == 0x00000010))
1875 {
1876 //if not compressed
1877 if ($arEntry['compressed_size'] == $arEntry['size'])
1878 {
1879 $string = fread($this->zipfile, $arEntry['compressed_size']);
1880 }
1881 else
1882 {
1883 $data = fread($this->zipfile, $arEntry['compressed_size']);
1884 $string = gzinflate($data);
1885 }
1886 }
1887 else
1888 {
1889 $this->_errorLog("ERR_EXTRACT", GetMessage("MAIN_ZIP_ERR_EXTRACT"));
1890 return $this->arErrors;
1891 }
1892
1893 return $res;
1894 }
1895
1896 private function _readFileHeader(&$arHeader)
1897 {
1898 $res = 1;
1899
1900 //read 4 bytes signature
1901 $binary_data = @fread($this->zipfile, 4);
1902
1903 $data = unpack('Vid', $binary_data);
1904
1905 //check signature
1906 if ($data['id'] != 0x04034b50)
1907 {
1908 $this->_errorLog("ERR_BAD_FORMAT", GetMessage("MAIN_ZIP_ERR_STRUCT"));
1909 return $this->arErrors;
1910 }
1911
1912 //reading first 42 bytes of the header
1913 $binary_data = fread($this->zipfile, 26);
1914
1915 //look for invalid block size
1916 if (strlen($binary_data) != 26)
1917 {
1918 $arHeader['filename'] = "";
1919 $arHeader['status'] = "invalid_header";
1920
1921 $this->_errorLog("ERR_BAD_BLOCK_SIZE", str_replace("#BLOCK_SIZE#", $binary_data, GetMessage("MAIN_ZIP_ERR_BLOCK_SIZE")));
1922 return $this->arErrors;
1923 }
1924
1925 //extract values
1926 $data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data);
1927
1928 $arHeader['filename'] = fread($this->zipfile, $data['filename_len']);
1929
1930 //extra fields
1931 if ($data['extra_len'] != 0)
1932 {
1933 $arHeader['extra'] = fread($this->zipfile, $data['extra_len']);
1934 }
1935 else
1936 {
1937 $arHeader['extra'] = '';
1938 }
1939
1940 //extract properties
1941 $arHeader['compression'] = $data['compression'];
1942 $arHeader['size'] = $data['size'];
1943 $arHeader['compressed_size'] = $data['compressed_size'];
1944 $arHeader['crc'] = $data['crc'];
1945 $arHeader['flag'] = $data['flag'];
1946
1947 //save date in unix format
1948 $arHeader['mdate'] = $data['mdate'];
1949 $arHeader['mtime'] = $data['mtime'];
1950
1951 if ($arHeader['mdate'] && $arHeader['mtime'])
1952 {
1953 //extract time
1954 $hour = ($arHeader['mtime'] & 0xF800) >> 11;
1955 $min = ($arHeader['mtime'] & 0x07E0) >> 5;
1956 $sec = ($arHeader['mtime'] & 0x001F) * 2;
1957
1958 //...and date
1959 $year = (($arHeader['mdate'] & 0xFE00) >> 9) + 1980;
1960 $month = ($arHeader['mdate'] & 0x01E0) >> 5;
1961 $day = $arHeader['mdate'] & 0x001F;
1962
1963 //unix date format
1964 $arHeader['mtime'] = mktime($hour, $min, $sec, $month, $day, $year);
1965 }
1966 else
1967 {
1968 $arHeader['mtime'] = time();
1969 }
1970
1971 //to be checked: for(reset($data); $key = key($data); next($data)) { }
1972 $arHeader['stored_filename'] = $arHeader['filename'];
1973 $arHeader['status'] = "ok";
1974
1975 return $res;
1976 }
1977
1978 private function _readCentralFileHeader(&$arHeader)
1979 {
1980 $res = 1;
1981
1982 //reading 4 bytes signature
1983 $binary_data = @fread($this->zipfile, 4);
1984
1985 $data = unpack('Vid', $binary_data);
1986
1987 //checking signature
1988 if ($data['id'] != 0x02014b50)
1989 {
1990 $this->_errorLog("ERR_BAD_FORMAT", GetMessage("MAIN_ZIP_ERR_STRUCT"));
1991 return $this->arErrors;
1992 }
1993
1994 //reading first header 42 bytes
1995 $binary_data = fread($this->zipfile, 42);
1996
1997 //if block size is not valid
1998 if (strlen($binary_data) != 42)
1999 {
2000 $arHeader['filename'] = "";
2001 $arHeader['status'] = "invalid_header";
2002
2003 $this->_errorLog("ERR_BAD_BLOCK_SIZE", str_replace("#SIZE#", $binary_data, GetMessage("MAIN_ZIP_ERR_BLOCK_SIZE")));
2004 return $this->arErrors;
2005 }
2006
2007 //extract values
2008 $arHeader = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $binary_data);
2009
2010 //getting filename
2011 if ($arHeader['filename_len'] != 0)
2012 {
2013 $arHeader['filename'] = fread($this->zipfile, $arHeader['filename_len']);
2014 }
2015 else
2016 {
2017 $arHeader['filename'] = '';
2018 }
2019
2020 //getting extra
2021 if ($arHeader['extra_len'] != 0)
2022 {
2023 $arHeader['extra'] = fread($this->zipfile, $arHeader['extra_len']);
2024 }
2025 else
2026 {
2027 $arHeader['extra'] = '';
2028 }
2029
2030 //getting comments
2031 if ($arHeader['comment_len'] != 0)
2032 {
2033 $arHeader['comment'] = fread($this->zipfile, $arHeader['comment_len']);
2034 }
2035 else
2036 {
2037 $arHeader['comment'] = '';
2038 }
2039
2040 //extracting properties
2041
2042 //saving date in unix format
2043 if ($arHeader['mdate'] && $arHeader['mtime'])
2044 {
2045 //extracting time
2046 $hour = ($arHeader['mtime'] & 0xF800) >> 11;
2047 $min = ($arHeader['mtime'] & 0x07E0) >> 5;
2048 $sec = ($arHeader['mtime'] & 0x001F) * 2;
2049
2050 //...and date
2051 $year = (($arHeader['mdate'] & 0xFE00) >> 9) + 1980;
2052 $month = ($arHeader['mdate'] & 0x01E0) >> 5;
2053 $day = $arHeader['mdate'] & 0x001F;
2054
2055 //in unix date format
2056 $arHeader['mtime'] = mktime($hour, $min, $sec, $month, $day, $year);
2057 }
2058 else
2059 {
2060 $arHeader['mtime'] = time();
2061 }
2062
2063 //set stored filename
2064 $arHeader['stored_filename'] = $arHeader['filename'];
2065
2066 //default status is 'ok'
2067 $arHeader['status'] = 'ok';
2068
2069 //is directory?
2070 if (str_ends_with($arHeader['filename'], '/'))
2071 {
2072 $arHeader['external'] = 0x41FF0010;
2073 }
2074
2075 return $res;
2076 }
2077
2078 private function _readEndCentralDir(&$arCentralDir)
2079 {
2080 $res = 1;
2081
2082 //going to the end of the file
2083 $size = filesize($this->io->GetPhysicalName($this->zipname));
2084 @fseek($this->zipfile, $size);
2085
2086 if (@ftell($this->zipfile) != $size)
2087 {
2088 $this->_errorLog("ERR_ARC_END", str_replace("#FILE_NAME#", removeDocRoot($this->zipname), GetMessage("MAIN_ZIP_ERR_ARC_END")));
2089 return $this->arErrors;
2090 }
2091
2092 //if archive is without comments (usually), the end of central dir is at 22 bytes of the file end
2093 $isFound = 0;
2094 $pos = 0;
2095
2096 if ($size > 26)
2097 {
2098 @fseek($this->zipfile, $size - 22);
2099
2100 if (@ftell($this->zipfile) != ($size - 22))
2101 {
2102 $this->_errorLog("ERR_ARC_MID", str_replace("#FILE_NAME#", removeDocRoot($this->zipname), GetMessage("MAIN_ZIP_ERR_ARC_MID")));
2103 return $this->arErrors;
2104 }
2105
2106 //read 4 bytes
2107 $binary_data = @fread($this->zipfile, 4);
2108 $data = unpack('Vid', $binary_data);
2109
2110 //signature check
2111 if ($data['id'] == 0x06054b50)
2112 {
2113 $isFound = 1;
2114 }
2115
2116 $pos = ftell($this->zipfile);
2117 }
2118
2119 //going back to the max possible size of the Central Dir End Record
2120 if (!$isFound)
2121 {
2122 $maxSize = 65557; // 0xFFFF + 22;
2123
2124 if ($maxSize > $size)
2125 {
2126 $maxSize = $size;
2127 }
2128
2129 @fseek($this->zipfile, $size - $maxSize);
2130
2131 if (@ftell($this->zipfile) != ($size - $maxSize))
2132 {
2133 $this->_errorLog("ERR_ARC_MID", str_replace("#FILE_NAME#", removeDocRoot($this->zipname), GetMessage("MAIN_ZIP_ERR_ARC_MID")));
2134 return $this->arErrors;
2135 }
2136
2137 //reading byte per byte to find the signature
2138 $pos = ftell($this->zipfile);
2139 $bytes = 0x00000000;
2140 while ($pos < $size)
2141 {
2142 //reading 1 byte
2143 $byte = @fread($this->zipfile, 1);
2144 //0x03000000504b0506 -> 0x504b0506
2145 $bytes = ($bytes << (8 * (PHP_INT_SIZE - 3))) >> (8 * (PHP_INT_SIZE - 4));
2146 //adding the byte
2147 $bytes = $bytes | ord($byte);
2148 //compare bytes
2149 if ($bytes == 0x504b0506)
2150 {
2151 $pos++;
2152 break;
2153 }
2154
2155 $pos++;
2156 }
2157
2158 //if end of the central dir is not found
2159 if ($pos == $size)
2160 {
2161 $this->_errorLog("ERR_ARC_MID_END", GetMessage("MAIN_ZIP_ERR_ARC_MID_END"));
2162 return $this->arErrors;
2163 }
2164 }
2165
2166 //reading first 18 bytes of the header
2167 $binary_data = fread($this->zipfile, 18);
2168
2169 //if block size is not valid
2170 if (strlen($binary_data) != 18)
2171 {
2172 $this->_errorLog("ERR_ARC_END_SIZE", str_replace("#SIZE#", mb_strlen($binary_data), GetMessage("MAIN_ZIP_ERR_ARC_END_SIZE")));
2173 return $this->arErrors;
2174 }
2175
2176 //extracting values
2177 $data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $binary_data);
2178
2179 //checking global size
2180 if (($pos + $data['comment_size'] + 18) != $size)
2181 {
2182 $this->_errorLog("ERR_SIGNATURE", GetMessage("MAIN_ZIP_ERR_SIGNATURE"));
2183 return $this->arErrors;
2184 }
2185
2186 //reading comments
2187 if ($data['comment_size'] != 0)
2188 {
2189 $arCentralDir['comment'] = fread($this->zipfile, $data['comment_size']);
2190 }
2191 else
2192 {
2193 $arCentralDir['comment'] = '';
2194 }
2195
2196 $arCentralDir['entries'] = $data['entries'];
2197 $arCentralDir['disk_entries'] = $data['disk_entries'];
2198 $arCentralDir['offset'] = $data['offset'];
2199 $arCentralDir['size'] = $data['size'];
2200 $arCentralDir['disk'] = $data['disk'];
2201 $arCentralDir['disk_start'] = $data['disk_start'];
2202
2203 return $res;
2204 }
2205
2206 private function _deleteByRule(&$arResultList, $arParams)
2207 {
2208 $arCentralDirInfo = [];
2209 $arHeaders = [];
2210
2211 if (($res = $this->_openFile('rb')) != 1)
2212 {
2213 return $res;
2214 }
2215
2216 if (($res = $this->_readEndCentralDir($arCentralDirInfo)) != 1)
2217 {
2218 $this->_closeFile();
2219 return $res;
2220 }
2221
2222 //scanning all the files, starting at the beginning of Central Dir
2223 $entryPos = $arCentralDirInfo['offset'];
2224 @rewind($this->zipfile);
2225
2226 if (@fseek($this->zipfile, $entryPos))
2227 {
2228 //clean file
2229 $this->_closeFile();
2230 $this->_errorLog("ERR_INVALID_ARCHIVE_ZIP", GetMessage("MAIN_ZIP_ERR_INVALID_ARCHIVE_ZIP"));
2231 return $this->arErrors;
2232 }
2233
2234 $j_start = 0;
2235
2236 //reading each entry
2237 for ($i = 0, $extractedCounter = 0; $i < $arCentralDirInfo['entries']; $i++)
2238 {
2239 //reading file header
2240 $arHeaders[$extractedCounter] = [];
2241
2242 $res = $this->_readCentralFileHeader($arHeaders[$extractedCounter]);
2243 if ($res != 1)
2244 {
2245 $this->_closeFile();
2246 return $res;
2247 }
2248
2249 //saving index
2250 $arHeaders[$extractedCounter]['index'] = $i;
2251
2252 //check specific extract rules
2253 $isFound = false;
2254
2255 //name rule
2256 if ((isset($arParams['by_name'])) && is_array($arParams['by_name']))
2257 {
2258 //if the filename is in the list
2259 for ($j = 0, $n = count($arParams['by_name']); $j < $n && !$isFound; $j++)
2260 {
2261 if (str_ends_with($arParams['by_name'][$j], "/"))
2262 {
2263 //if the directory is in the filename path
2264 if ((mb_strlen($arHeaders[$extractedCounter]['stored_filename']) > mb_strlen($arParams['by_name'][$j]))
2265 && (str_starts_with($arHeaders[$extractedCounter]['stored_filename'], $arParams['by_name'][$j])))
2266 {
2267 $isFound = true;
2268 }
2269 elseif ((($arHeaders[$extractedCounter]['external'] & 0x00000010) == 0x00000010) /* Indicates a folder */
2270 && ($arHeaders[$extractedCounter]['stored_filename'] . '/' == $arParams['by_name'][$j]))
2271 {
2272 $isFound = true;
2273 }
2274 }
2275 elseif ($arHeaders[$extractedCounter]['stored_filename'] == $arParams['by_name'][$j])
2276 {
2277 //check filename
2278 $isFound = true;
2279 }
2280 }
2281 }
2282 else
2283 {
2284 if ((isset($arParams['by_preg'])) && ($arParams['by_preg'] != ""))
2285 {
2286 if (preg_match($arParams['by_preg'], $arHeaders[$extractedCounter]['stored_filename']))
2287 {
2288 $isFound = true;
2289 }
2290 }
2291 else
2292 {
2293 if ((isset($arParams['by_index'])) && is_array($arParams['by_index']))
2294 {
2295 //index rule: if index is in the list
2296 for ($j = $j_start, $n = count($arParams['by_index']); $j < $n && !$isFound; $j++)
2297 {
2298 if (($i >= $arParams['by_index'][$j]['start'])
2299 && ($i <= $arParams['by_index'][$j]['end']))
2300 {
2301 $isFound = true;
2302 }
2303 if ($i >= $arParams['by_index'][$j]['end'])
2304 {
2305 $j_start = $j + 1;
2306 }
2307 if ($arParams['by_index'][$j]['start'] > $i)
2308 {
2309 break;
2310 }
2311 }
2312 }
2313 }
2314 }
2315
2316 //delete?
2317 if ($isFound)
2318 {
2319 unset($arHeaders[$extractedCounter]);
2320 }
2321 else
2322 {
2323 $extractedCounter++;
2324 }
2325 }
2326
2327 //if something should be deleted
2328 if ($extractedCounter > 0)
2329 {
2330 //create tmp file
2331 $zipname_tmp = GetDirPath($this->zipname) . uniqid('ziparc') . '.tmp';
2332 //create tmp zip archive
2333 $tmpzip = new CZip($zipname_tmp);
2334
2335 if (($res = $tmpzip->_openFile('wb')) != 1)
2336 {
2337 $this->_closeFile();
2338 return $res;
2339 }
2340
2341 //check which file should be kept
2342 for ($i = 0; $i < sizeof($arHeaders); $i++)
2343 {
2344 //calculate the position of the header
2345 @rewind($this->zipfile);
2346 if (@fseek($this->zipfile, $arHeaders[$i]['offset']))
2347 {
2348 $this->_closeFile();
2349 $tmpzip->_closeFile();
2350 @unlink($this->io->GetPhysicalName($zipname_tmp));
2351 $this->_errorLog("ERR_INVALID_ARCHIVE_ZIP", GetMessage("MAIN_ZIP_ERR_INVALID_ARCHIVE_ZIP"));
2352
2353 return $this->arErrors;
2354 }
2355
2356 if (($res = $this->_readFileHeader($arHeaders[$i])) != 1)
2357 {
2358 $this->_closeFile();
2359 $tmpzip->_closeFile();
2360 @unlink($this->io->GetPhysicalName($zipname_tmp));
2361
2362 return $res;
2363 }
2364
2365 //writing file header
2366 $res = $tmpzip->_writeFileHeader($arHeaders[$i]);
2367 if ($res != 1)
2368 {
2369 $this->_closeFile();
2370 $tmpzip->_closeFile();
2371 @unlink($this->io->GetPhysicalName($zipname_tmp));
2372
2373 return $res;
2374 }
2375
2376 //reading/writing data block
2377 $res = $this->_copyBlocks($this->zipfile, $tmpzip->zipfile, $arHeaders[$i]['compressed_size']);
2378 if ($res != 1)
2379 {
2380 $this->_closeFile();
2381 $tmpzip->_closeFile();
2382 @unlink($this->io->GetPhysicalName($zipname_tmp));
2383
2384 return $res;
2385 }
2386 }
2387
2388 //save central dir offset
2389 $offset = @ftell($tmpzip->zipfile);
2390
2391 //re-write central dir files header
2392 for ($i = 0; $i < sizeof($arHeaders); $i++)
2393 {
2394 $res = $tmpzip->_writeCentralFileHeader($arHeaders[$i]);
2395 if ($res != 1)
2396 {
2397 $tmpzip->_closeFile();
2398 $this->_closeFile();
2399 @unlink($this->io->GetPhysicalName($zipname_tmp));
2400
2401 return $res;
2402 }
2403
2404 //convert header to the 'usable' format
2405 $tmpzip->_convertHeader2FileInfo($arHeaders[$i], $arResultList[$i]);
2406 }
2407
2408 $zip_comment = '';
2409 $size = @ftell($tmpzip->zipfile) - $offset;
2410
2411 $res = $tmpzip->_writeCentralHeader(sizeof($arHeaders), $size, $offset, $zip_comment);
2412 if ($res != 1)
2413 {
2414 unset($arHeaders);
2415 $tmpzip->_closeFile();
2416 $this->_closeFile();
2417 @unlink($this->io->GetPhysicalName($zipname_tmp));
2418
2419 return $res;
2420 }
2421
2422 $tmpzip->_closeFile();
2423 $this->_closeFile();
2424
2425 //deleting zip file (result should be checked)
2426 @unlink($this->io->GetPhysicalName($this->zipname));
2427
2428 //result should be checked
2429 $this->_renameTmpFile($zipname_tmp, $this->zipname);
2430
2431 unset($tmpzip);
2432 }
2433
2434 return $res;
2435 }
2436
2437 private function _checkDir($dir, $isDir = false)
2438 {
2439 $res = 1;
2440
2441 //remove '/' at the end
2442 if (($isDir) && (str_ends_with($dir, '/')))
2443 {
2444 $dir = substr($dir, 0, -1);
2445 }
2446
2447 //check if dir is available
2448 if ((is_dir($dir)) || ($dir == ""))
2449 {
2450 return 1;
2451 }
2452
2453 //get parent directory
2454 $parentDir = dirname($dir);
2455
2456 if ($parentDir != $dir)
2457 {
2458 //find the parent dir
2459 if ($parentDir != "")
2460 {
2461 if (($res = $this->_checkDir($parentDir)) != 1)
2462 {
2463 return $res;
2464 }
2465 }
2466 }
2467
2468 //creating a directory
2469 if (!@mkdir($dir))
2470 {
2471 $this->_errorLog("ERR_DIR_CREATE_FAIL", str_replace("#DIR_NAME#", $dir, GetMessage("MAIN_ZIP_ERR_DIR_CREATE_FAIL")));
2472 return $this->arErrors;
2473 }
2474
2475 return $res;
2476 }
2477
2478 private function _checkParams(&$arParams, $arDefaultValues)
2479 {
2480 if (!is_array($arParams))
2481 {
2482 $this->_errorLog("ERR_PARAM", GetMessage("MAIN_ZIP_ERR_PARAM"));
2483 return $this->arErrors;
2484 }
2485
2486 //all params should be valid
2487 foreach ($arParams as $key => $dummy)
2488 {
2489 if (!isset($arDefaultValues[$key]))
2490 {
2491 $this->_errorLog("ERR_PARAM_KEY", str_replace("#KEY#", $key, GetMessage("MAIN_ZIP_ERR_PARAM_KEY")));
2492 return $this->arErrors;
2493 }
2494 }
2495
2496 //set default values
2497 foreach ($arDefaultValues as $key => $value)
2498 {
2499 if (!isset($arParams[$key]))
2500 {
2502 }
2503 }
2504
2505 //check specific parameters
2506 $arCallbacks = ['callback_pre_add', 'callback_post_add', 'callback_pre_extract', 'callback_post_extract'];
2507
2508 for ($i = 0; $i < sizeof($arCallbacks); $i++)
2509 {
2510 $key = $arCallbacks[$i];
2511
2512 if ((isset($arParams[$key])) && ($arParams[$key] != ''))
2513 {
2514 if (!function_exists($arParams[$key]))
2515 {
2516 $this->_errorLog("ERR_PARAM_CALLBACK", str_replace(["#CALLBACK#", "#PARAM_NAME#"], [$arParams[$key], $key], GetMessage("MAIN_ZIP_ERR_PARAM_CALLBACK")));
2517 return $this->arErrors;
2518 }
2519 }
2520 }
2521
2522 return (1);
2523 }
2524
2525 private function _errorLog($errorName, $errorString = '')
2526 {
2527 $this->arErrors[] = "[" . $errorName . "] " . $errorString;
2528 }
2529
2530 private function _errorReset()
2531 {
2532 $this->arErrors = [];
2533 }
2534
2535 private function _reducePath($dir)
2536 {
2537 $res = "";
2538
2539 if ($dir != "")
2540 {
2541 //get directory names
2542 $arTmpList = explode("/", $dir);
2543
2544 //check from last to first
2545 for ($i = sizeof($arTmpList) - 1; $i >= 0; $i--)
2546 {
2547 //is current path
2548 if ($arTmpList[$i] == ".")
2549 {
2550 //just ignore. the first $i should be = 0, but no check is done
2551 }
2552 else
2553 {
2554 if ($arTmpList[$i] == "..")
2555 {
2556 //ignore this and ignore the $i-1
2557 $i--;
2558 }
2559 else
2560 {
2561 if (($arTmpList[$i] == "") && ($i != (sizeof($arTmpList) - 1)) && ($i != 0))
2562 {
2563 //ignore only the double '//' in path, but not the first and last '/'
2564 }
2565 else
2566 {
2567 $res = $arTmpList[$i] . ($i != (sizeof($arTmpList) - 1) ? "/" . $res : "");
2568 }
2569 }
2570 }
2571 }
2572 }
2573 return $res;
2574 }
2575
2576 private function _containsPath($dir, $path)
2577 {
2578 $res = 1;
2579
2580 //explode dir and path by directory separator
2581 $arTmpDirList = explode("/", $dir);
2582 $arTmpPathList = explode("/", $path);
2583
2584 $arTmpDirListSize = sizeof($arTmpDirList);
2585 $arTmpPathListSize = sizeof($arTmpPathList);
2586
2587 //check dir paths
2588 $i = 0;
2589 $j = 0;
2590
2591 while (($i < $arTmpDirListSize) && ($j < $arTmpPathListSize) && ($res))
2592 {
2593 //check if is empty
2594 if ($arTmpDirList[$i] == '')
2595 {
2596 $i++;
2597 continue;
2598 }
2599
2600 if ($arTmpPathList[$j] == '')
2601 {
2602 $j++;
2603 continue;
2604 }
2605
2606 //compare items
2607 if ($arTmpDirList[$i] != $arTmpPathList[$j])
2608 {
2609 $res = 0;
2610 }
2611
2612 $i++;
2613 $j++;
2614 }
2615
2616 //check if the same
2617 if ($res)
2618 {
2619 //skip empty items
2620 while (($j < $arTmpPathListSize) && ($arTmpPathList[$j] == ''))
2621 {
2622 $j++;
2623 }
2624
2625 while (($i < $arTmpDirListSize) && ($arTmpDirList[$i] == ''))
2626 {
2627 $i++;
2628 }
2629
2630 if (($i >= $arTmpDirListSize) && ($j >= $arTmpPathListSize))
2631 {
2632 //exactly the same
2633 $res = 2;
2634 }
2635 else
2636 {
2637 if ($i < $arTmpDirListSize)
2638 {
2639 //path is shorter than the dir
2640 $res = 0;
2641 }
2642 }
2643 }
2644
2645 return $res;
2646 }
2647
2648 private function _copyBlocks($source, $dest, $blockSize, $mode = 0)
2649 {
2650 $res = 1;
2651
2652 if ($mode == 0)
2653 {
2654 while ($blockSize != 0)
2655 {
2656 $length = ($blockSize < self::ReadBlockSize ? $blockSize : self::ReadBlockSize);
2657 $buffer = @fread($source, $length);
2658 @fwrite($dest, $buffer, $length);
2659 $blockSize -= $length;
2660 }
2661 }
2662 else
2663 {
2664 if ($mode == 1)
2665 {
2666 while ($blockSize != 0)
2667 {
2668 $length = ($blockSize < self::ReadBlockSize ? $blockSize : self::ReadBlockSize);
2669 $buffer = @gzread($source, $length);
2670 @fwrite($dest, $buffer, $length);
2671 $blockSize -= $length;
2672 }
2673 }
2674 else
2675 {
2676 if ($mode == 2)
2677 {
2678 while ($blockSize != 0)
2679 {
2680 $length = ($blockSize < self::ReadBlockSize ? $blockSize : self::ReadBlockSize);
2681 $buffer = @fread($source, $length);
2682 @gzwrite($dest, $buffer, $length);
2683 $blockSize -= $length;
2684 }
2685 }
2686 else
2687 {
2688 if ($mode == 3)
2689 {
2690 while ($blockSize != 0)
2691 {
2692 $length = ($blockSize < self::ReadBlockSize ? $blockSize : self::ReadBlockSize);
2693 $buffer = @gzread($source, $length);
2694 @gzwrite($dest, $buffer, $length);
2695 $blockSize -= $length;
2696 }
2697 }
2698 }
2699 }
2700 }
2701
2702 return $res;
2703 }
2704
2705 private function _renameTmpFile($source, $dest)
2706 {
2707 $res = 1;
2708
2709 if (!@rename($this->io->GetPhysicalName($source), $this->io->GetPhysicalName($dest)))
2710 {
2711 if (!@copy($this->io->GetPhysicalName($source), $this->io->GetPhysicalName($dest)))
2712 {
2713 $res = 0;
2714 }
2715 else
2716 {
2717 if (!@unlink($this->io->GetPhysicalName($source)))
2718 {
2719 $res = 0;
2720 }
2721 }
2722 }
2723
2724 return $res;
2725 }
2726
2727 private function _convertWinPath($path, $removeDiskLetter = true)
2728 {
2729 if (mb_stristr(php_uname(), 'windows'))
2730 {
2731 //disk letter?
2732 if ($removeDiskLetter && ($position = mb_strpos($path, ':')) !== false)
2733 {
2734 $path = mb_substr($path, $position + 1);
2735 }
2736
2737 //change windows directory separator
2738 if ((strpos($path, '\\') > 0) || (str_starts_with($path, '\\')))
2739 {
2740 $path = strtr($path, '\\', '/');
2741 }
2742 }
2743
2744 return $path;
2745 }
2746
2747 private function _parseFileParams($arFileList)
2748 {
2749 if (isset($arFileList) && is_array($arFileList))
2750 {
2751 return $arFileList;
2752 }
2753
2754 if (isset($arFileList) && $arFileList <> '')
2755 {
2756 if (str_starts_with($arFileList, "\""))
2757 {
2758 return [trim($arFileList, "\"")];
2759 }
2760 return explode(" ", $arFileList);
2761 }
2762
2763 return [];
2764 }
2765
2766 private function _cleanFile()
2767 {
2768 $this->_closeFile();
2769 @unlink($this->io->GetPhysicalName($this->zipname));
2770 }
2771
2772 private function _checkDirPath($path)
2773 {
2774 $path = str_replace(["\\", "//"], "/", $path);
2775
2776 //remove file name
2777 if (!str_ends_with($path, "/"))
2778 {
2779 $p = mb_strrpos($path, "/");
2780 $path = mb_substr($path, 0, $p);
2781 }
2782
2783 $path = rtrim($path, "/");
2784
2785 if (!file_exists($this->io->GetPhysicalName($path)))
2786 {
2787 return mkdir($this->io->GetPhysicalName($path), BX_DIR_PERMISSIONS, true);
2788 }
2789 else
2790 {
2791 return is_dir($this->io->GetPhysicalName($path));
2792 }
2793 }
2794
2795 private function _getfileSystemEncoding()
2796 {
2797 $fileSystemEncoding = mb_strtolower(defined("BX_FILE_SYSTEM_ENCODING") ? BX_FILE_SYSTEM_ENCODING : "");
2798
2799 if (empty($fileSystemEncoding))
2800 {
2801 if (mb_strtoupper(mb_substr(PHP_OS, 0, 3)) === "WIN")
2802 {
2803 $fileSystemEncoding = "windows-1251";
2804 }
2805 else
2806 {
2807 $fileSystemEncoding = "utf-8";
2808 }
2809 }
2810
2811 return $fileSystemEncoding;
2812 }
2813}
$arParams
Определения access_dialog.php:21
$path
Определения access_edit.php:21
$count
Определения admin_tab.php:4
static IsFileSafe(string $filename)
Определения archive.php:167
static HasAccess($strFilename, $isFile)
Определения archive.php:141
Определения virtual_io.php:44
static GetInstance()
Определения virtual_io.php:60
Определения zip.php:12
$zipname
Определения zip.php:15
$zipfile
Определения zip.php:16
SetOptions($arOptions)
Определения zip.php:451
GetOptions()
Определения zip.php:493
Delete($arParams)
Определения zip.php:693
GetStartFile()
Определения zip.php:396
Extract($arParams=0)
Определения zip.php:649
Create($arFileList, $arParams=0)
Определения zip.php:523
Unpack($strPath)
Определения zip.php:406
GetErrors()
Определения zip.php:512
__construct($pzipname)
Определения zip.php:34
const ReadBlockSize
Определения zip.php:13
GetProperties()
Определения zip.php:728
GetContent()
Определения zip.php:625
Add($arFileList, $arParams=0)
Определения zip.php:574
Pack($arFileList, $startFile="")
Определения zip.php:48
$content
Определения commerceml.php:144
$bytes
Определения cron_html_pages.php:17
$data['IS_AVAILABLE']
Определения .description.php:13
if(!defined("ADMIN_AJAX_MODE") &&(($_REQUEST["mode"] ?? '') !='excel')) $buffer
Определения epilog_admin_after.php:40
$filename
Определения file_edit.php:47
$fileContent
Определения file_property.php:47
$res
Определения filter_act.php:7
$handle
Определения include.php:55
$result
Определения get_property_values.php:14
$p
Определения group_list_element_edit.php:23
if(CIMConvert::ConvertCount() > 0) $arDefaultValues['default']
Определения options.php:26
Определения archive.php:4
const StatusSuccess
Определения archive.php:6
const StatusError
Определения archive.php:5
const StatusContinue
Определения archive.php:7
$arOptions
Определения structure.php:223
removeDocRoot($path)
Определения tools.php:3382
GetDirPath($sPath)
Определения tools.php:3245
IncludeModuleLangFile($filepath, $lang=false, $bReturnArray=false)
Определения tools.php:3778
GetMessage($name, $aReplace=null)
Определения tools.php:3397
_normalizePath($strPath)
Определения tools.php:3341
$value
Определения Param.php:39
$year
Определения payment.php:9
$counter
Определения options.php:5
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$dir
Определения quickway.php:303
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
if(!empty($sellerData)) $dest
Определения pdf.php:818
$comment
Определения template.php:15
$arCallbacks
Определения seo_page_parser.php:12
$n
Определения update_log.php:107
if( $site[ 'SERVER_NAME']==='') if($site['SERVER_NAME']==='') $arProperties
Определения yandex_run.php:644