PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 1.6.9
JetBackup – Backup, Restore & Migrate v1.6.9
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 / SGArchive.php
backup / com / lib Last commit date
BackupGuard 5 years ago Dropbox 3 years ago Request 5 years ago SGArchive.php 4 years ago SGAuthClient.php 4 years ago SGCallback.php 5 years ago SGCdrEntry.php 9 years ago SGCharsetHandler.php 7 years ago SGDBState.php 8 years ago SGEntry.php 9 years ago SGFileEntry.php 5 years ago SGFileState.php 6 years ago SGMigrateState.php 8 years ago SGMysqldump.php 5 years ago SGReloadHandler.php 4 years ago SGReloader.php 5 years ago SGReloaderState.php 9 years ago SGReviewManager.php 6 years ago SGState.php 5 years ago SGStatsRequests.php 5 years ago SGUploadHandler.php 5 years ago SGUploadState.php 5 years ago
SGArchive.php
800 lines
1 <?php
2
3 interface SGArchiveDelegate
4 {
5 public function getCorrectCdrFilename($filename);
6
7 public function didExtractFile($filePath);
8
9 public function didCountFilesInsideArchive($count);
10
11 public function didFindExtractError($error);
12
13 public function warn($message);
14
15 public function didExtractArchiveMeta($meta);
16
17 public function didStartRestoreFiles();
18 }
19
20 class SGArchive // phpcs:ignore
21 {
22 const VERSION = 5;
23 const CHUNK_SIZE = 1048576; //1mb
24 private $_filePath = '';
25 private $_mode = '';
26 private $_fileHandle = null;
27 private $_cdrFileHandle = null;
28 private $_cdrFilesCount = 0;
29 private $_cdr = array();
30 private $_fileOffset = 0;
31 private $_delegate;
32 private $_ranges = array();
33 private $_state = null;
34 private $_rangeCursor = 0;
35 private $_cdrOffset = 0;
36
37 public function __construct($filePath, $mode, $cdrSize = 0)
38 {
39 $this->_filePath = $filePath;
40 $this->_mode = $mode;
41 $this->_fileHandle = @fopen($filePath, $mode . 'b');
42 $this->clear();
43
44 if ($cdrSize) {
45 $this->_cdrFilesCount = $cdrSize;
46 }
47
48 if ($mode == 'a') {
49 $cdrPath = $filePath . '.cdr';
50
51 $this->_cdrFileHandle = @fopen($cdrPath, $mode . 'b');
52 }
53 }
54
55 public function setDelegate(SGArchiveDelegate $delegate)
56 {
57 $this->_delegate = $delegate;
58 }
59
60 public function getCdrFilesCount()
61 {
62 return $this->_cdrFilesCount;
63 }
64
65 public function addFileFromPath($filename, $path)
66 {
67 $headerSize = 0;
68 $len = 0;
69 $zlen = 0;
70 $start = 0;
71
72 $fp = fopen($path, 'rb');
73 $fileSize = backupGuardRealFilesize($path);
74
75 $state = $this->_delegate->getState();
76 $offset = $state->getOffset();
77
78 if (!$state->getInprogress()) {
79 $headerSize = $this->addFileHeader();
80 } else {
81 $headerSize = $state->getHeaderSize();
82 $this->_fileOffset = $state->getFileOffsetInArchive();
83 }
84
85 $this->_ranges = $state->getRanges();
86 if (count($this->_ranges)) {
87 $range = end($this->_ranges); //get last range of file
88
89 $start += $range['start'] + $range['size'];
90 $zlen = $start; // get file compressed size before reload
91 }
92
93 fseek($fp, $offset); // move to point before reload
94 //read file in small chunks
95 while ($offset < $fileSize) {
96 $data = fread($fp, self::CHUNK_SIZE);
97 if ($data === '') {
98 //When fread fails to read and compress on fly
99 if ($zlen == 0 && $fileSize != 0 && strlen($data) == 0) {
100 $this->_delegate->warn('Failed to read file: ' . basename($filename));
101 }
102 break;
103 }
104
105 $data = gzdeflate($data);
106 $zlen += strlen($data);
107 $sgArchiveSize = backupGuardRealFilesize($this->_filePath);
108 $sgArchiveSize += strlen($data);
109
110 if ($sgArchiveSize > SG_ARCHIVE_MAX_SIZE_32) {
111 SGBoot::checkRequirement('intSize');
112 }
113
114 $this->write($data);
115
116 array_push(
117 $this->_ranges,
118 array(
119 'start' => $start,
120 'size' => strlen($data)
121 )
122 );
123 $offset = ftell($fp);
124
125 $start += strlen($data);
126
127 SGPing::update();
128 $shouldReload = $this->_delegate->shouldReload();
129 if ($shouldReload) {
130 $this->_delegate->saveStateData(SG_STATE_ACTION_COMPRESSING_FILES, $this->_ranges, $offset, $headerSize, true, $this->_fileOffset);
131
132 if (backupGuardIsReloadEnabled()) {
133 @fclose($fp);
134 @fclose($this->_fileHandle);
135 @fclose($this->_cdrFileHandle);
136
137 $this->_delegate->reload();
138 }
139 }
140 }
141
142 if ($state->getInprogress()) {
143 $headerSize = $state->getHeaderSize();
144 }
145
146 SGPing::update();
147
148 fclose($fp);
149
150 $this->addFileToCdr($filename, $zlen, $len, $headerSize);
151 }
152
153 public function addFile($filename, $data)
154 {
155 $headerSize = $this->addFileHeader();
156
157 if ($data) {
158 $data = gzdeflate($data);
159 $this->write($data);
160 }
161
162 $zlen = strlen($data);
163 $len = 0;
164
165 $this->addFileToCdr($filename, $zlen, $len, $headerSize);
166 }
167
168 private function addFileHeader()
169 {
170 //save extra
171 $extra = '';
172
173 $extraLengthInBytes = 4;
174 $this->write($this->packToLittleEndian(strlen($extra), $extraLengthInBytes) . $extra);
175
176 return $extraLengthInBytes + strlen($extra);
177 }
178
179 private function addFileToCdr($filename, $zlen, $len, $headerSize)
180 {
181 //store cdr data for later use
182 $this->addToCdr($filename, $zlen, $len);
183
184 $this->_fileOffset += $headerSize + $zlen;
185 }
186
187 public function finalize()
188 {
189 $this->addFooter();
190
191 fclose($this->_fileHandle);
192
193 $this->clear();
194 }
195
196 private function addFooter()
197 {
198 $footer = '';
199
200 //save version
201 $footer .= $this->packToLittleEndian(self::VERSION, 1);
202
203 $tables = SGConfig::get('SG_BACKUPED_TABLES');
204
205 if ($tables) {
206 $tables = json_encode($tables);
207 } else {
208 $tables = "";
209 }
210
211 $multisitePath = "";
212 $multisiteDomain = "";
213
214 if (SG_ENV_ADAPTER == SG_ENV_WORDPRESS) {
215 // in case of multisite save old path and domain for later usage
216 if (is_multisite()) {
217 $multisitePath = PATH_CURRENT_SITE;
218 $multisiteDomain = DOMAIN_CURRENT_SITE;
219 }
220 }
221
222 //save db prefix, site and home url for later use
223 $extra = json_encode(
224 array(
225 'siteUrl' => get_site_url(),
226 'home' => get_home_url(),
227 'dbPrefix' => SG_ENV_DB_PREFIX,
228 'tables' => $tables,
229 'method' => SGConfig::get('SG_BACKUP_TYPE'),
230 'multisitePath' => $multisitePath,
231 'multisiteDomain' => $multisiteDomain,
232 'selectivRestoreable' => true,
233 'phpVersion' => phpversion()
234 )
235 );
236
237 //extra size
238 $footer .= $this->packToLittleEndian(strlen($extra), 4) . $extra;
239
240 //save cdr size
241 $footer .= $this->packToLittleEndian($this->_cdrFilesCount, 4);
242
243 $this->write($footer);
244
245 //save cdr
246 $cdrLen = $this->writeCdr();
247
248 //save offset to the start of footer
249 $len = $cdrLen + strlen($extra) + 13;
250 $this->write($this->packToLittleEndian($len, 4));
251 }
252
253 private function writeCdr()
254 {
255 @fclose($this->_cdrFileHandle);
256
257 $cdrLen = 0;
258 $fp = @fopen($this->_filePath . '.cdr', 'rb');
259
260 while (!feof($fp)) {
261 $data = fread($fp, self::CHUNK_SIZE);
262 $cdrLen += strlen($data);
263 $this->write($data);
264 }
265
266 @fclose($fp);
267 @unlink($this->_filePath . '.cdr');
268
269 return $cdrLen;
270 }
271
272 private function clear()
273 {
274 $this->_cdr = array();
275 $this->_fileOffset = 0;
276 $this->_cdrFilesCount = 0;
277 }
278
279 private function addToCdr($filename, $compressedLength, $uncompressedLength)
280 {
281 $rec = $this->packToLittleEndian(0, 4); //crc (not used in this version)
282 $rec .= $this->packToLittleEndian(strlen($filename), 2);
283 $rec .= $filename;
284 // file offset, compressed length, uncompressed length all are writen in 8 bytes to cover big integer size
285 $rec .= $this->packToLittleEndian($this->_fileOffset, 8);
286 $rec .= $this->packToLittleEndian($compressedLength, 8);
287 $rec .= $this->packToLittleEndian($uncompressedLength, 8); //uncompressed size (not used in this version)
288 $rec .= $this->packToLittleEndian(count($this->_ranges), 4);
289
290 foreach ($this->_ranges as $range) {
291 // start and size all are writen in 8 bytes to cover big integer size
292 $rec .= $this->packToLittleEndian($range['start'], 8);
293 $rec .= $this->packToLittleEndian($range['size'], 8);
294 }
295
296 fwrite($this->_cdrFileHandle, $rec);
297 fflush($this->_cdrFileHandle);
298
299 $this->_cdrFilesCount++;
300 }
301
302 private function isEnoughFreeSpaceOnDisk($dataSize)
303 {
304 $freeSpace = false;
305
306 if (function_exists('disk_free_space')) {
307 $freeSpace = @disk_free_space(SG_APP_ROOT_DIRECTORY);
308 }
309
310 if ($freeSpace === false || $freeSpace === null) {
311 return true;
312 }
313
314 if ($freeSpace < $dataSize) {
315 return false;
316 }
317
318 return true;
319 }
320
321 private function write($data)
322 {
323 $isEnoughFreeSpaceOnDisk = $this->isEnoughFreeSpaceOnDisk(strlen($data));
324 if (!$isEnoughFreeSpaceOnDisk) {
325 throw new SGExceptionIO('Failed to write in the archive due to not sufficient disk free space.');
326 }
327
328 $result = fwrite($this->_fileHandle, $data);
329 if ($result === false) {
330 throw new SGExceptionIO('Failed to write in archive');
331 }
332 fflush($this->_fileHandle);
333 }
334
335 private function read($length)
336 {
337 $result = fread($this->_fileHandle, $length);
338 if ($result === false) {
339 throw new SGExceptionIO('Failed to read from archive');
340 }
341 return $result;
342 }
343
344 private function packToLittleEndian($value, $size = 4)
345 {
346 if (is_int($value)) {
347 $size *= 2; //2 characters for each byte
348 $value = str_pad(dechex($value), $size, '0', STR_PAD_LEFT);
349 return strrev(pack('H' . $size, $value));
350 }
351
352 $hex = str_pad($value->toHex(), 16, '0', STR_PAD_LEFT);
353
354 $high = substr($hex, 0, 8);
355 $low = substr($hex, 8, 8);
356
357 $high = strrev(pack('H8', $high));
358 $low = strrev(pack('H8', $low));
359
360 return $low . $high;
361 }
362
363 public function getArchiveHeaders()
364 {
365 return $this->extractHeaders();
366 }
367
368 public function getFilesList()
369 {
370 $list = array();
371 $cdrSize = hexdec($this->unpackLittleEndian($this->read(4), 4));
372 $this->_cdrOffset = ftell($this->_fileHandle);
373
374 for ($i = 0; $i < $cdrSize; $i++) {
375 $el = $this->getNextCdrElement($this->_cdrOffset);
376 array_push($list, $el[0]);
377 }
378 return $list;
379 }
380
381 public function getTreefromList($list, $limit = "")
382 {
383 $tree = array();
384 if (end($list) == "./sql") {
385 array_pop($list);
386 }
387 for ($i = 0; $i < count($list); $i++) {
388 if (!backupGuardStringStartsWith($list[$i], $limit)) {
389 continue;
390 }
391 $path = substr($list[$i], strlen($limit));
392 $path = explode(DIRECTORY_SEPARATOR, $path);
393 $exists = false;
394 foreach ($tree as $el) {
395 if ($path[0] == $el->name) {
396 $exists = true;
397 break;
398 }
399 }
400 if (!$exists) {
401 $node = new stdClass();
402 $node->name = $path[0];
403 if (count($path) > 1) {
404 $node->type = "folder";
405 } else {
406 $node->type = "file";
407 }
408 array_push($tree, $node);
409 }
410 }
411 return $tree;
412 }
413
414 public function extractTo($destinationPath, $state = null)
415 {
416 $this->_state = $state;
417 $action = $state->getAction();
418
419 if ($action == SG_STATE_ACTION_PREPARING_STATE_FILE) {
420 $this->extract($destinationPath);
421 } else {
422 $this->continueExtract($destinationPath);
423 }
424 }
425
426 private function extractHeaders()
427 {
428 //read offset
429 fseek($this->_fileHandle, -4, SEEK_END);
430 $offset = hexdec($this->unpackLittleEndian($this->read(4), 4));
431
432 //read version
433 fseek($this->_fileHandle, -$offset, SEEK_END);
434 $version = hexdec($this->unpackLittleEndian($this->read(1), 1));
435 SGConfig::set('SG_CURRENT_ARCHIVE_VERSION', $version);
436
437 //read extra size (not used in this version)
438 $extraSize = hexdec($this->unpackLittleEndian($this->read(4), 4));
439
440 //read extra
441 $extra = array();
442 if ($extraSize > 0) {
443 $extra = $this->read($extraSize);
444 $extra = json_decode($extra, true);
445
446 SGConfig::set('SG_OLD_SITE_URL', $extra['siteUrl']);
447 SGConfig::set('SG_OLD_DB_PREFIX', $extra['dbPrefix']);
448
449 if (isset($extra['phpVersion'])) {
450 SGConfig::set('SG_OLD_PHP_VERSION', $extra['phpVersion']);
451 }
452
453 SGConfig::set('SG_BACKUPED_TABLES', $extra['tables']);
454 SGConfig::set('SG_BACKUP_TYPE', $extra['method']);
455
456 SGConfig::set('SG_MULTISITE_OLD_PATH', $extra['multisitePath']);
457 SGConfig::set('SG_MULTISITE_OLD_DOMAIN', $extra['multisiteDomain']);
458 }
459
460 $extra['version'] = $version;
461 return $extra;
462 }
463
464 private function extract($destinationPath)
465 {
466 $extra = $this->extractHeaders();
467 $version = $extra['version'];
468
469 $this->_delegate->didExtractArchiveMeta($extra);
470
471 $isMultisite = backupGuardIsMultisite();
472 $archiveIsMultisite = $extra['multisitePath'] != '' || $extra['multisiteDomain'] != '';
473
474 if (SG_ENV_ADAPTER == SG_ENV_WORDPRESS) {
475 if ($archiveIsMultisite && !$isMultisite) {
476 throw new SGExceptionMigrationError("In order to restore this archive you should set up Multisite WordPress!");
477 } elseif (!$archiveIsMultisite && $isMultisite) {
478 throw new SGExceptionMigrationError("In order to restore this archive you should set up a Standard instead of Multisite WordPress!");
479 }
480 }
481
482 if ($version >= SG_MIN_SUPPORTED_ARCHIVE_VERSION && $version <= SG_MAX_SUPPORTED_ARCHIVE_VERSION) {
483 if (!SGBoot::isFeatureAvailable('BACKUP_WITH_MIGRATION')) {
484 if ($extra['method'] != SG_BACKUP_METHOD_MIGRATE) {
485 if ($extra['siteUrl'] == SG_SITE_URL) {
486 if ($extra['dbPrefix'] != SG_ENV_DB_PREFIX) {
487 throw new SGException("Seems you have changed database prefix. You should keep it constant to be able to restore this backup. Setup your WordPress installation with " . $extra['dbPrefix'] . " datbase prefix.");
488 }
489 } else {
490 throw new SGExceptionMigrationError("You should install <b>BackupGuard Pro</b> to be able to migrate the website. More detailed information regarding features included in <b>Free</b> and <b>Pro</b> versions you can find here: <a href='" . SG_BACKUP_SITE_URL . "'>" . SG_BACKUP_SITE_URL . "</a>");
491 }
492 } else {
493 throw new SGExceptionMigrationError("You should install <b>BackupGuard Pro</b> to be able to restore a package designed for migration.More detailed information regarding features included in <b>Free</b> and <b>Pro</b> versions you can find here: <a href='" . SG_BACKUP_SITE_URL . "'>" . SG_BACKUP_SITE_URL . "</a>");
494 }
495 }
496 } else {
497 throw new SGExceptionBadRequest('Invalid SGArchive file');
498 }
499
500 //read cdr size
501 $this->_cdrFilesCount = hexdec($this->unpackLittleEndian($this->read(4), 4));
502
503 $this->_delegate->didStartRestoreFiles();
504 $this->_delegate->didCountFilesInsideArchive($this->_cdrFilesCount);
505
506 // $this->extractCdr($cdrSize, $destinationPath);
507 $this->_cdrOffset = ftell($this->_fileHandle);
508 $this->extractFiles($destinationPath);
509 }
510
511 private function continueExtract($destinationPath)
512 {
513 $this->_fileOffset = $this->_state->getOffset();
514 fseek($this->_fileHandle, $this->_fileOffset);
515 $this->extractFiles($destinationPath);
516 }
517
518 private function getNextCdrElement($offset)
519 {
520 fseek($this->_fileHandle, $this->_cdrOffset);
521 //read crc (not used in this version)
522 $this->read(4);
523
524 //read filename
525 $filenameLen = hexdec($this->unpackLittleEndian($this->read(2), 2));
526 $filename = $this->read($filenameLen);
527 $filename = $this->_delegate->getCorrectCdrFilename($filename);
528
529 //read file offset
530 $fileOffsetInArchive = $this->unpackLittleEndian($this->read(8), 8);
531 $fileOffsetInArchive = hexdec($fileOffsetInArchive);
532
533 //read compressed length
534 $zlen = $this->unpackLittleEndian($this->read(8), 8);
535 $zlen = hexdec($zlen);
536
537 //read uncompressed length (not used in this version)
538 $this->read(8);
539
540 $rangeLen = hexdec($this->unpackLittleEndian($this->read(4), 4));
541
542 $ranges = array();
543 for ($i = 0; $i < $rangeLen; $i++) {
544 $start = $this->unpackLittleEndian($this->read(8), 8);
545 $start = hexdec($start);
546
547 $size = $this->unpackLittleEndian($this->read(8), 8);
548 $size = hexdec($size);
549
550 $ranges[] = array(
551 'start' => $start,
552 'size' => $size
553 );
554 }
555
556 $this->_cdrOffset = ftell($this->_fileHandle);
557 return array($filename, $zlen, $ranges, $fileOffsetInArchive);
558 }
559
560 private function extractFiles($destinationPath)
561 {
562 $action = $this->_state->getAction();
563 if ($action == SG_STATE_ACTION_PREPARING_STATE_FILE) {
564 $inprogress = false;
565 fseek($this->_fileHandle, 0, SEEK_SET);
566 } else {
567 $inprogress = $this->_state->getInprogress();
568 $this->_cdrFilesCount = $this->_state->getCdrSize();
569 $this->_cdrOffset = $this->_state->getCdrCursor();
570 }
571
572 $sqlFileEnding = $this->_state->getBackupFileName() . '/' . $this->_state->getBackupFileName() . '.sql';
573 $restoreMode = $this->_state->getRestoreMode();
574 $restoreFiles = $this->_state->getRestoreFiles();
575
576 while ($this->_cdrFilesCount) {
577 $warningFoundDuringExtract = false;
578
579 if ($inprogress) {
580 $row = $this->_state->getCdr();
581 } else {
582 $row = $this->getNextCdrElement($this->_cdrOffset);
583
584 fseek($this->_fileHandle, $this->_fileOffset);
585
586 //read extra (not used in this version)
587 $this->read(4);
588 }
589
590 $path = $destinationPath . $row[0];
591 $path = str_replace('\\', '/', $path);
592 $restoreCurrentFile = false;
593
594 if ($restoreMode == SG_RESTORE_MODE_FILES && $restoreFiles != null && count($restoreFiles) > 0) {
595 for ($j = 0; $j < count($restoreFiles); $j++) {
596 if ($restoreFiles[$j] == "/" || backupGuardStringStartsWith($row[0], $restoreFiles[$j])) {
597 $restoreCurrentFile = true;
598 break;
599 }
600 }
601 }
602
603 // check if file should be restored according restore mode selected by user
604 if ($restoreMode == SG_RESTORE_MODE_FULL || ($restoreMode == SG_RESTORE_MODE_DB && backupGuardStringEndsWith($path, $sqlFileEnding)) || ($restoreMode == SG_RESTORE_MODE_FILES && !backupGuardStringEndsWith($path, $sqlFileEnding) && $restoreCurrentFile)) {
605 if ($path[strlen($path) - 1] != '/') {//it's not an empty directory
606 $path = dirname($path);
607 }
608
609 if (!$inprogress) {
610 if (!$this->createPath($path)) {
611 $ranges = $row[2];
612
613 //get last range of file
614 $range = end($ranges);
615 $offset = $range['start'] + $range['size'];
616
617 // skip file and continue
618 fseek($this->_fileHandle, $offset, SEEK_CUR);
619 $this->_delegate->didFindExtractError('Could not create directory: ' . dirname($path));
620 continue;
621 }
622 }
623
624 $path = $destinationPath . $row[0];
625 $tmpPath = $path . ".sgbpTmpFile";
626
627 if (!$inprogress) {
628 $this->_delegate->didStartExtractFile($path);
629
630 if (!is_writable(dirname($tmpPath))) {
631 $this->_delegate->didFindExtractError('Destination path is not writable: ' . dirname($path));
632 }
633 }
634
635 if (!$inprogress) {
636 $tmpFp = @fopen($tmpPath, 'wb');
637 } else {
638 $tmpFp = @fopen($tmpPath, 'ab');
639 }
640
641 $zlen = $row[1]; // phpcs:ignore
642 SGPing::update();
643 $ranges = $row[2];
644
645 if ($inprogress) {
646 $this->_rangeCursor = $this->_state->getRangeCursor();
647 } else {
648 $this->_rangeCursor = 0;
649 }
650
651 for ($i = $this->_rangeCursor; $i < count($ranges); $i++) {
652 $start = $ranges[$i]['start']; // phpcs:ignore
653 $size = $ranges[$i]['size'];
654
655 $data = $this->read($size);
656 $data = gzinflate($data);
657
658 //If gzinflate() failed to uncompress, skip the current file and continue extraction
659 if (!$data) {
660 $warningFoundDuringExtract = true;
661 $this->_delegate->didFindExtractError('Failed to extract path: ' . $path);
662
663 //Assume we've extracted the current file
664 for ($idx = $i + 1; $idx < count($ranges); $idx++) {
665 $start = $ranges[$idx]['start']; // phpcs:ignore
666 $size = $ranges[$idx]['size'];
667
668 fseek($this->_fileHandle, $size, SEEK_CUR);
669 }
670
671 $inprogress = false;
672 @fclose($tmpFp);
673
674 SGPing::update();
675
676 break;
677 } else {
678 $inprogress = true;
679 if (($i + 1) == count($ranges)) {
680 $inprogress = false;
681 }
682 if (is_resource($tmpFp)) {
683 $isEnoughFreeSpaceOnDisk = $this->isEnoughFreeSpaceOnDisk(strlen($data));
684 if (!$isEnoughFreeSpaceOnDisk) {
685 throw new SGExceptionIO('Failed to write in the archive due to not sufficient disk free space.');
686 }
687
688 fwrite($tmpFp, $data);
689 fflush($tmpFp);
690
691 $shouldReload = $this->_delegate->shouldReload();
692
693 //restore with reloads will only work in external mode
694 if ($shouldReload && SGExternalRestore::isEnabled()) {
695 if (!$inprogress) {
696 $this->_cdrFilesCount--;
697
698 @rename($tmpPath, $path);
699 $this->_delegate->didExtractFile($path);
700 }
701
702 $token = $this->_delegate->getToken();
703 $progress = $this->_delegate->getProgress();
704
705 $this->_fileOffset = ftell($this->_fileHandle);
706
707 $this->_state->setRestoreMode($restoreMode);
708 $this->_state->setOffset($this->_fileOffset);
709 $this->_state->setInprogress($inprogress);
710 $this->_state->setToken($token);
711 $this->_state->setProgress($progress);
712 $this->_state->setAction(SG_STATE_ACTION_RESTORING_FILES);
713 $this->_state->setRangeCursor($i + 1);
714
715 $this->_state->setCdr($row);
716 $this->_state->setCdrSize($this->_cdrFilesCount);
717 $this->_state->setCdrCursor($this->_cdrOffset);
718 $this->_state->save();
719
720 SGPing::update();
721
722 @fclose($tmpFp);
723 @fclose($this->_fileHandle);
724
725 $this->_delegate->reload();
726 }
727 }
728 }
729 SGPing::update();
730 }
731
732 if (is_resource($tmpFp)) {
733 @fclose($tmpFp);
734 }
735
736 if (!$warningFoundDuringExtract) {
737 @rename($tmpPath, $path);
738 } else {
739 @unlink($tmpPath);
740 }
741
742 $this->_delegate->didExtractFile($path);
743 $this->_fileOffset = ftell($this->_fileHandle);
744 } else {
745 //if file should not be restored skip it and go to the next file
746 $ranges = $row[2];
747
748 for ($idx = 0; $idx < count($ranges); $idx++) {
749 $size = $ranges[$idx]['size'];
750
751 fseek($this->_fileHandle, $size, SEEK_CUR);
752 }
753 $this->_fileOffset = ftell($this->_fileHandle);
754 }
755
756 $this->_cdrFilesCount--;
757 }
758 }
759
760 private function unpackLittleEndian($data, $size)
761 {
762 $size *= 2; //2 characters for each byte
763
764 $data = unpack('H' . $size, strrev($data));
765 return $data[1];
766 }
767
768 private function createPath($path)
769 {
770 if (is_dir($path)) {
771 return true;
772 }
773 $prevPath = substr($path, 0, strrpos($path, '/', -2) + 1);
774 $return = $this->createPath($prevPath);
775 if ($return && is_writable($prevPath)) {
776 if (!@mkdir($path)) {
777 return false;
778 }
779
780 @chmod($path, 0777);
781 return true;
782 }
783
784 return false;
785 }
786
787 public function getVersion()
788 {
789 //read offset
790 fseek($this->_fileHandle, -4, SEEK_END);
791 $offset = hexdec($this->unpackLittleEndian($this->read(4), 4));
792
793 //read version
794 fseek($this->_fileHandle, -$offset, SEEK_END);
795 $version = hexdec($this->unpackLittleEndian($this->read(1), 1));
796
797 return $version;
798 }
799 }
800