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