PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 2.0.3
JetBackup – Backup, Restore & Migrate v2.0.3
3.1.22.4 3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / com / lib / BackupGuard / Core / SGBGArchive.php
backup / com / lib / BackupGuard / Core Last commit date
ISGArchiveDelegate.php 3 years ago SGBGArchive.php 3 years ago SGBGArchiveCdr.php 3 years ago SGBGArchiveHelper.php 3 years ago SGBGCache.php 3 years ago SGBGCacheableFile.php 3 years ago SGBGDirectoryTreeFile.php 3 years ago SGBGFile.php 3 years ago SGBGFileHelper.php 3 years ago SGBGJsonFile.php 3 years ago SGBGLog.php 3 years ago SGBGReloader.php 3 years ago SGBGStateFile.php 3 years ago SGBGTask.php 3 years ago
SGBGArchive.php
840 lines
1 <?php
2
3 require_once(__DIR__ . '/ISGArchiveDelegate.php');
4 require_once(__DIR__ . '/SGBGArchiveHelper.php');
5 require_once(__DIR__ . '/SGBGArchiveCdr.php');
6 require_once(__DIR__ . '/SGBGCacheableFile.php');
7 require_once(__DIR__ . '/SGBGTask.php');
8 require_once(__DIR__ . '/SGBGLog.php');
9
10 class SGBGArchive extends SGBGCacheableFile
11 {
12 const VERSION = 5;
13 const CHUNK_SIZE = 4 * 1024 * 1024; //4mb
14 private $currentFileOffset = 0;
15 private $cdrFile = null;
16 private $delegate = null;
17 private $task = null;
18 private $logFile = null;
19 private $logEnabled = false;
20 private $_excludePaths = array();
21 private $_warningsFound = false;
22 private $_options = array();
23 private $_cdrOffset = 0;
24 private $_cdrSize = 0;
25
26 public function __construct($path)
27 {
28 parent::__construct($path);
29 }
30
31 public function setLogFile($logFile)
32 {
33 $this->logFile = $logFile;
34 }
35
36 public function getLogFile()
37 {
38 return $this->logFile;
39 }
40
41 public function setLogEnabled($logEnabled)
42 {
43 $this->logEnabled = $logEnabled;
44 }
45
46 public function getLogEnabled()
47 {
48 return $this->logEnabled;
49 }
50
51 public function setExcludePaths($paths)
52 {
53 $this->_excludePaths = $paths;
54 }
55
56 public function getExcludePaths()
57 {
58 return $this->_excludePaths;
59 }
60
61 public function setOptions($options)
62 {
63 $this->_options = $options;
64 }
65
66 public function getOptions()
67 {
68 return $this->_options;
69 }
70
71 public function log($logData, $forceWrite = false)
72 {
73 if ($this->getLogEnabled() || $forceWrite) {
74 $this->getLogFile()->save($logData);
75 }
76 }
77
78 public function open($mode)
79 {
80 //only write (w) and read (r) modes supported
81 //convert all modes to binary
82 if (strpos($mode, 'w') === 0) {
83 $mode = 'ab';
84 } else if (strpos($mode, 'r') === 0) {
85 $mode = 'rb';
86 } else {
87 throw new Exception('Archive open mode not supported');
88 }
89
90 $this->log('Open archive (mode: ' . $mode . ')');
91
92 parent::open($mode);
93
94 if ($mode == 'ab') {
95 $this->openCdrFile($this->getPath() . '.cdr');
96 }
97 }
98
99 public function close()
100 {
101 if ($this->cdrFile) {
102 $this->log('Close CDR file');
103
104 $this->cdrFile->close();
105 }
106
107 $this->log('Close archive');
108
109 parent::close();
110 }
111
112 private function openCdrFile($cdrPath)
113 {
114 $this->log('Open CDR file');
115
116 $file = new SGBGArchiveCdr($cdrPath);
117 $file->getCache()->setCacheMode(SGBGCache::CACHE_MODE_TIMEOUT | SGBGCache::CACHE_MODE_SIZE);
118 $file->getCache()->setCacheTimeout(5);
119 $file->getCache()->setCacheSize(4000000);
120 $file->open('ab');
121
122 //load cdr size from state
123 $cdrSize = (int) $this->task->getStateFile()->getData('cdr_size');
124 $file->setCount($cdrSize);
125
126 $this->cdrFile = $file;
127 }
128
129 public function setDelegate($delegate)
130 {
131 $this->delegate = $delegate;
132 if ($delegate instanceof ISGArchiveDelegate) {
133
134 }
135 }
136
137 public function setTask($task)
138 {
139 if ($task instanceof SGBGTask) {
140 $this->task = $task;
141 }
142 }
143
144 private function willAddFile($filename, $path)
145 {
146 if ($this->delegate) {
147 $this->delegate->willAddFile($filename);
148 }
149 }
150
151 private function willAddFileChunk($filename, $path)
152 {
153 if ($this->delegate) {
154 $this->delegate->willAddFileChunk($filename);
155 }
156 }
157
158 private function didAddFileChunk($filename, $path, $chunk)
159 {
160 if ($this->delegate) {
161 $this->delegate->didAddFileChunk($filename, $chunk);
162 }
163 }
164
165 private function didAddFile($filename, $path)
166 {
167 if ($this->delegate) {
168 $this->delegate->didAddFile($filename);
169 }
170 }
171
172 public function didFindWarnings()
173 {
174 return $this->_warningsFound;
175 }
176
177 public function addFileFromPath($filename, $path)
178 {
179 if ($path != '') {
180 $this->log('');
181 $this->log('Start add file ' . $filename, true);
182
183 $this->willAddFile($filename, $path);
184
185 $fp = @fopen($path, 'rb');
186 if ($fp === false) {
187 $this->warn('Failed to open file: ' . $path);
188 return;
189 }
190
191 $fileSize = SGBGArchiveHelper::realFilesize($path);
192 $stateFile = $this->task->getStateFile();
193
194 $fileChunks = (array) $stateFile->getData('chunks');
195 $chunkOffset = (int) $stateFile->getData('chunk_offset');
196 $srcFileOffset = (int) $stateFile->getData('src_file_offset');
197 $this->currentFileOffset = (int) $stateFile->getData('file_offset');
198
199 $this->log('File size: ' . $fileSize);
200 $this->log('File offset in archive: ' . $this->currentFileOffset);
201
202 if ($srcFileOffset) {
203 fseek($fp, $srcFileOffset);
204 $this->log('Seek src file: ' . $srcFileOffset);
205 } else {
206 $this->addFileHeader();
207 }
208
209 //read file by chunks
210 while (!feof($fp)) {
211 $this->log('Start add file chunk');
212 $this->willAddFileChunk($filename, $path);
213
214 $data = @fread($fp, self::CHUNK_SIZE);
215 if ($data === false) {
216 $this->warn('Failed to read file: ' . basename($filename));
217 } else if (empty($data)) {
218 break;
219 }
220
221 $chunk = $this->addFileChunk($data, $chunkOffset);
222 $fileChunks[] = $chunk;
223 $chunkOffset += $chunk['size'];
224
225 $this->didAddFileChunk($filename, $path, $chunk);
226
227 $this->log('End add file chunk: Start(' . $chunk['start'] . ') - Size(' . $chunk['size'] . ')');
228 $this->log('Next chunk offset: ' . $chunkOffset);
229
230 $this->task->continueTask(function () use ($fileChunks, $chunkOffset, &$stateFile, &$fp) {
231 $this->log('Exit task (to resume later)', true);
232
233 //flush cache before exit
234 $this->getCache()->flush();
235 $this->cdrFile->getCache()->flush();
236 $this->close();
237
238 $stateFile->setData('chunks', $fileChunks);
239 $stateFile->setData('chunk_offset', $chunkOffset);
240 $srcFileOffset = ftell($fp);
241 $stateFile->setData('src_file_offset', $srcFileOffset);
242
243 $this->log('Src file offset: ' . $srcFileOffset);
244 $this->getLogFile()->getCache()->flush();
245 $this->getLogFile()->close();
246 $this->updateProgress();
247
248 @fclose($fp);
249 });
250 }
251
252 @fclose($fp);
253
254 $fp = @fopen($path, 'rb');
255 $firstBytes = fread($fp, 100);
256 fseek($fp, -100, SEEK_END);
257 $lastBytes = fread($fp, 100);
258
259 @fclose($fp);
260 $crcData = $firstBytes . $lastBytes;
261
262
263 $this->cdrFile->addFile(
264 $filename,
265 $this->currentFileOffset,
266 $chunkOffset, //total compressed length
267 $fileSize,
268 $fileChunks,
269 array($this, 'log'),
270 $crcData
271 );
272
273 $fileHeaderSize = 4;
274 $this->currentFileOffset += $fileHeaderSize + $chunkOffset;
275 $this->log('Next file offset in archive will be: ' . $this->currentFileOffset);
276
277 $stateFile->setData('file_offset', $this->currentFileOffset);
278 $stateFile->setData('cdr_size', $this->cdrFile->getCount());
279
280 //reset state file data
281 $stateFile->setData('chunks', array());
282 $stateFile->setData('chunk_offset', 0);
283 $stateFile->setData('src_file_offset', 0);
284 $stateFile->save(false);
285
286 $this->didAddFile($filename, $path);
287
288 $this->log('End add file ' . $filename, true);
289 }
290
291 }
292
293 public function addEmptyDirectory($filename)
294 {
295 //IMPORTANT: empty directory names must end with a slash
296 $filename = rtrim($filename, '/') . '/';
297
298 if ($this->delegate) {
299 $this->delegate->willAddFile($filename);
300 }
301
302 $stateFile = $this->task->getStateFile();
303
304 $fileHeaderSize = $this->addFileHeader();
305
306
307 $this->cdrFile->addFile(
308 $filename,
309 $this->currentFileOffset,
310 0,
311 0,
312 array(),
313 array($this, 'log'),
314 ""
315 );
316
317 $this->currentFileOffset += $fileHeaderSize;
318 $this->log('Next file offset in archive will be: ' . $this->currentFileOffset);
319
320 $stateFile->setData('file_offset', $this->currentFileOffset);
321 $stateFile->setData('cdr_size', $this->cdrFile->getCount());
322
323 //reset state file data
324 $stateFile->setData('chunks', array());
325 $stateFile->setData('chunk_offset', 0);
326 $stateFile->setData('src_file_offset', 0);
327 $stateFile->save(false);
328
329
330 if ($this->delegate) {
331 $this->delegate->didAddFile($filename);
332 }
333
334 $this->log('End add Empty Directory');
335 }
336
337 private function addFileChunk($data, $chunkOffset)
338 {
339 $this->log('Read: ' . strlen($data) . ' bytes');
340
341 $data = gzdeflate($data);
342
343 $this->log('Compressed: ' . strlen($data) . ' bytes');
344
345 $result = $this->write($data);
346
347 $this->log('Written: ' . $result . ' bytes');
348
349 return array(
350 'start' => $chunkOffset,
351 'size' => strlen($data)
352 );
353 }
354
355 private function addFileHeader()
356 {
357 $this->log('Start add header');
358
359 $extra = '';
360
361 $extraLengthInBytes = 4;
362 $this->write(SGBGArchiveHelper::packToLittleEndian(strlen($extra), $extraLengthInBytes) . $extra);
363
364 $headerSize = ($extraLengthInBytes + strlen($extra));
365
366 $this->log('End add header: ' . $headerSize . ' bytes');
367
368 return $headerSize;
369 }
370
371 private function addFooter()
372 {
373 $footer = '';
374
375 //save version
376 $footer .= SGBGArchiveHelper::packToLittleEndian(self::VERSION, 1);
377
378 $extra = '';
379
380 if ($this->delegate) {
381 $extra = $this->delegate->getArchiveExtraData();
382 }
383
384 //extra size
385 $footer .= SGBGArchiveHelper::packToLittleEndian(strlen($extra), 4) . $extra;
386
387 //save cdr size
388 $cdrSize = (int) $this->task->getStateFile()->getData('cdr_size');
389 $footer .= SGBGArchiveHelper::packToLittleEndian($cdrSize, 4);
390
391 $this->write($footer);
392
393 //save cdr
394 $cdrLen = $this->writeCdr();
395
396 //save offset to the start of footer
397 $len = $cdrLen + strlen($extra) + 13;
398 $this->write(SGBGArchiveHelper::packToLittleEndian($len, 4));
399 }
400
401 private function writeCdr()
402 {
403 $this->cdrFile->getCache()->flush();
404 $this->cdrFile->close();
405
406 $cdrLen = 0;
407 $this->cdrFile->open('rb');
408
409 while (!$this->cdrFile->eof()) {
410 $data = $this->cdrFile->read(self::CHUNK_SIZE);
411 $cdrLen += strlen($data);
412 $this->write($data);
413 }
414
415 //@fclose($fp);
416 $this->cdrFile->close();
417 $this->cdrFile->remove();
418
419 return $cdrLen;
420 }
421
422 public function finalize()
423 {
424 $this->addFooter();
425 $this->getCache()->flush();
426
427 $this->log('Finalized');
428
429 $this->getLogFile()->getCache()->flush();
430 $this->updateProgress();
431 $this->close();
432
433 }
434
435 public function getHeaders()
436 {
437 return $this->extractHeaders();
438 }
439
440 private function extractHeaders()
441 {
442 $this->log('Start extract headers');
443
444 //read offset
445 $this->seek(-4, SEEK_END);
446 $offset = hexdec(SGBGArchiveHelper::unpackLittleEndian($this->read(4), 4));
447
448 $this->log('Footer offset: ' . $offset);
449
450 //read version
451 $this->seek(-$offset, SEEK_END);
452 $version = hexdec(SGBGArchiveHelper::unpackLittleEndian($this->read(1), 1));
453
454 $this->log('Version: ' . $version);
455
456 if ($version < self::VERSION) {
457 throw new Exception('Invalid SGArchive file version.');
458 }
459
460 //read extra size
461 $extraSize = hexdec(SGBGArchiveHelper::unpackLittleEndian($this->read(4), 4));
462
463 $this->log('Extra size: ' . $extraSize);
464
465 //read extra
466 $extra = '';
467 if ($extraSize > 0) {
468 $extra = $this->read($extraSize);
469 }
470
471 $this->log('Extra read: ' . strlen($extra) . ' bytes');
472
473 if(is_string($extra)) {
474 $extra = json_decode($extra, true);
475 if(is_string($extra['tables'])) {
476 $extra['tables'] = json_decode($extra['tables'], true);
477 }
478 }
479
480 if ($this->delegate) {
481 $this->delegate->didExtractArchiveHeaders($version, $extra);
482 }
483
484 if (is_array($extra)) {
485 $extra['versions'] = $version;
486 }
487
488 $this->log('End extract headers');
489
490 return $extra;
491 }
492
493 public function getFilesList()
494 {
495 $this->extractHeaders();
496
497 $list = array();
498 $cdrSize = hexdec(SGBGArchiveHelper::unpackLittleEndian($this->read(4), 4));
499
500 for ($i = 0; $i < $cdrSize; $i++) {
501 $list[] = $this->getNextCdrItem();
502 }
503
504 return $list;
505 }
506
507 private function getNextCdrItem()
508 {
509 $this->log('Start read CDR item');
510
511 //read crc (not used in this version)
512 $crc = $this->read(4);
513
514 //$this->log('CRC: Not supported');
515
516 //read filename
517 $filenameLen = SGBGArchiveHelper::unpackLittleEndian($this->read(2), 2);
518 $filenameLen = hexdec($filenameLen);
519
520 $this->log('Filename length: '.$filenameLen);
521
522 if ($filenameLen > 0) {
523 $filename = $this->read($filenameLen);
524
525
526 if ($this->delegate) {
527 $filename = $this->delegate->getCorrectCdrFilename($filename);
528 }
529
530 $this->log('Filename: ' . $filename);
531
532 //read file offset
533 $fileOffsetInArchive = SGBGArchiveHelper::unpackLittleEndian($this->read(8), 8);
534 $fileOffsetInArchive = hexdec($fileOffsetInArchive);
535
536 $this->log('File offset: ' . $fileOffsetInArchive);
537
538 //read compressed length
539 $compressedLength = SGBGArchiveHelper::unpackLittleEndian($this->read(8), 8);
540 $compressedLength = hexdec($compressedLength);
541
542 $this->log('Compressed length: ' . $compressedLength);
543
544 //read uncompressed length
545 $uncompressedLength = SGBGArchiveHelper::unpackLittleEndian($this->read(8), 8);
546 $uncompressedLength = hexdec($uncompressedLength);
547
548 $this->log('Uncompressed length: ' . $uncompressedLength);
549
550 //read number of chunks
551 $chunksCount = hexdec(SGBGArchiveHelper::unpackLittleEndian($this->read(4), 4));
552
553 $this->log('Number of chunks: ' . $chunksCount);
554
555 $chunks = array();
556 for ($i = 0; $i < $chunksCount; $i++) {
557 $start = SGBGArchiveHelper::unpackLittleEndian($this->read(8), 8);
558 $start = hexdec($start);
559
560 $size = SGBGArchiveHelper::unpackLittleEndian($this->read(8), 8);
561 $size = hexdec($size);
562
563 $this->log('Chunk ' . ($i + 1) . ': Start(' . $start . ') - Size(' . $size . ')');
564
565 $chunks[] = array(
566 'start' => $start,
567 'size' => $size
568 );
569 }
570
571 $this->log('End read CDR item');
572
573 return array(
574 'filename' => $filename,
575 'offset' => $fileOffsetInArchive,
576 'compressedLength' => $compressedLength,
577 'uncompressedLength' => $uncompressedLength,
578 'chunks' => $chunks,
579 'crc' => $crc
580 );
581 } else {
582 return [];
583 }
584 }
585
586 public function extractTo($destinationPath)
587 {
588 $resumingRestore = $this->task->getStateFile()->getStatus() != SGBGStateFile::STATUS_READY;
589
590 $this->log('Start extract', !$resumingRestore);
591
592 $this->extractHeaders();
593
594 //read cdr size
595 $cdrSize = hexdec(SGBGArchiveHelper::unpackLittleEndian($this->read(4), 4));
596
597 $this->log('CDR size: ' . $cdrSize, false);
598
599 $this->task->start($cdrSize);
600
601 if ($this->delegate) {
602 $this->delegate->didCountFilesInsideArchive($cdrSize);
603 }
604
605 $this->extractFiles($destinationPath, $cdrSize, $resumingRestore);
606 }
607
608 private function extractFiles($destinationPath, $cdrSize, $resumingRestore)
609 {
610 $stateFile = $this->task->getStateFile();
611
612 $this->log('Start extract files', !$resumingRestore);
613
614 $cdrOffset = $stateFile->getData('cdrOffset');
615 if ($resumingRestore) {
616 $this->_cdrSize = (int) $stateFile->getData('cdrSize');
617 $this->_cdrOffset = (int) $cdrOffset;
618 $this->seek($this->_cdrOffset);
619 } else {
620 $this->_cdrSize = $cdrSize;
621 }
622
623 while ($this->_cdrSize) {
624 $cdrItem = $this->getNextCdrItem();
625
626 if ($cdrItem) {
627 //we remember where we left the cdr, to come back to that point
628 $this->_cdrOffset = $this->tell();
629
630 $this->extractFile($cdrItem, $destinationPath);
631
632 //coming back to the cdr
633 $this->seek($this->_cdrOffset);
634
635 $this->_cdrSize--;
636
637 $stateFile->setData('cdrOffset', $this->_cdrOffset);
638 $stateFile->setData('cdrSize', $this->_cdrSize);
639 $stateFile->save(false);
640 }
641 }
642
643 $this->log('End extract files', true);
644 }
645
646 private function createTmpFile($path)
647 {
648 $file = new SGBGCacheableFile($path);
649 $file->getCache()->setCacheMode(SGBGCache::CACHE_MODE_TIMEOUT | SGBGCache::CACHE_MODE_SIZE);
650 $file->getCache()->setCacheTimeout(10);
651 $file->getCache()->setCacheSize(8000000);
652 $file->open('ab');
653 return $file;
654 }
655
656 private function extractFile($cdrItem, $destinationPath)
657 {
658 $this->log('Start extract file: ' . $cdrItem['filename'], true);
659
660 //$this->seek($cdrItem['offset']);
661
662 $this->log('Start read file header');
663
664 //read extra (not used in this version)
665 //$this->read(4);
666
667 $this->log('Extra: Not supported');
668 $this->log('End read file header');
669
670 $path = $destinationPath . $cdrItem['filename'];
671 $path = str_replace('\\', '/', $path);
672
673 $isEmptyDirectory = false;
674 $destPath = $path;
675 if (substr($path, -1) != '/') { //it's not an empty directory
676 $destPath = dirname($path);
677 } else {
678 $isEmptyDirectory = true;
679 }
680
681 $this->log('Destination path: ' . $path);
682 $this->log('Is empty directory: ' . ($isEmptyDirectory ? 'Yes' : 'No'));
683
684 if ($this->delegate) {
685 if ($this->delegate->shouldExtractFile($path)) {
686 $this->delegate->willExtractFile($path);
687 } else {
688 $this->log('Skip file extract', true);
689 return true;
690 }
691 }
692
693 $this->log('Prepare destination path (create folders recursively)');
694
695 if (!SGBGArchiveHelper::createPath($destPath)) {
696 if ($this->delegate) {
697 $this->delegate->didFindExtractError('Could not create directory for: ' . $destPath);
698 }
699 return false;
700 }
701
702 if ($isEmptyDirectory) {
703 $this->log('End extract file', true);
704 return true;
705 }
706
707 $tmpPath = $path . '.sgbpTmpFile';
708 $tmpFile = $this->createTmpFile($tmpPath);
709
710 $this->log('Create tmp file: ' . $tmpPath);
711
712 $errorFound = false;
713
714 $stateFile = $this->task->getStateFile();
715 $chunkNumber = (int) $stateFile->getData('chunkNumber');
716
717 $chunks = array_slice($cdrItem['chunks'], $chunkNumber);
718 //$this->log(print_r($chunks, true), true);
719
720 foreach ($chunks as $i => $chunk) {
721 $this->seek($cdrItem['offset'] + 4 + $chunk['start']);
722
723 $this->task->continueTask(function () use ($stateFile, $tmpFile, $chunkNumber, $i) {
724 $this->log('Extract Exit task (to resume later)', true);
725 $this->log('###_Extract_OffSet_###', true);
726
727 $tmpFile->getCache()->flush();
728 $tmpFile->close();
729
730 $this->close();
731
732 $stateFile->setData('chunkNumber', $chunkNumber + $i);
733 $stateFile->setStatus(SGBGStateFile::STATUS_RESUME);
734 $stateFile->save(true);
735
736 $this->getLogFile()->getCache()->flush();
737 $this->getLogFile()->close();
738 $this->updateProgress();
739 });
740
741 if (!$this->extractFileChunk($chunk, $tmpFile)) {
742 $errorFound = true;
743 break;
744 }
745 }
746
747 $tmpFile->getCache()->flush();
748 $tmpFile->close();
749
750 $this->task->endChunk();
751
752 $stateFile->setData('chunkNumber', 0);
753 $stateFile->save(true);
754
755 //CRC check here
756 /*$ff = fopen($tmpPath, 'r');
757 $firstBytes = fread($ff, 100);
758 fseek($ff, -100, SEEK_END);
759 $lastBytes = fread($ff, 100);
760
761 if (abs(crc32($firstBytes . $lastBytes)) != hexdec(SGBGArchiveHelper::unpackLittleEndian($cdrItem['crc'], 4))) {
762 $this->log("invalid CRC Header in file " . $cdrItem['filename']);
763 $this->log(abs(crc32($firstBytes . $lastBytes)) . '!=');
764 $this->log(hexdec(SGBGArchiveHelper::unpackLittleEndian($cdrItem['crc'], 4)));
765 } else {
766 $this->log("CRC Check Complete");
767 }*/
768
769 if (!$errorFound) {
770 $errorFound = !@rename($tmpPath, $path);
771 }
772
773 if ($errorFound) {
774 $tmpFile->remove();
775 $this->log('Failed to extract file: ' . $path, true);
776
777 if ($this->delegate) {
778 $this->delegate->didFindExtractError('Failed to extract path: ' . $path);
779 }
780 } else if ($this->delegate) {
781 $this->delegate->didExtractFile($path);
782 }
783
784 $this->log('End extract file: ' . $cdrItem['filename'], true);
785
786 return !$errorFound;
787 }
788
789 private function extractFileChunk($chunk, $tmpFile)
790 {
791 $start = $chunk['start'];
792 $size = $chunk['size'];
793
794 $this->log('Start extract chunk: Start(' . $start . ') - Size(' . $size . ')');
795
796 $data = $this->read($size);
797 $this->log('Read: ' . strlen($data) . ' bytes');
798 $data = gzinflate($data);
799
800 //if gzinflate() failed to uncompress, skip the current file and continue extraction
801 if (!$data) {
802 $this->log('Failed to uncompress');
803 return false;
804 }
805
806 $this->log('Uncompressed: ' . strlen($data) . ' bytes');
807
808 $result = $tmpFile->write($data);
809
810 $this->log('Written: ' . $result . ' bytes');
811 $this->log('End extract chunk');
812
813 return ($result !== false);
814 }
815
816 private function updateProgress()
817 {
818 if ($this->task->getStateFile()->getCount() && $this->task->getStateFile()->getCount() > 0) {
819 $progress = round($this->task->getStateFile()->getOffset() * 100.0 / $this->task->getStateFile()->getCount());
820
821 if ($this->delegate) {
822 $this->delegate->didUpdateProgress($progress);
823 }
824 }
825
826 return true;
827 }
828
829 private function pathWithoutRootDirectory($path)
830 {
831 return substr($path, strlen(rtrim(SGConfig::get('SG_APP_ROOT_DIRECTORY'), '/') . '/'));
832 }
833
834 public function warn($message)
835 {
836 $this->_warningsFound = true;
837 $this->log('Warning: ' . $message, true);
838 }
839 }
840