PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 3.1.22.3
JetBackup – Backup, Restore & Migrate v3.1.22.3
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 / src / JetBackup / Restore / Restore.php
backup / src / JetBackup / Restore Last commit date
.htaccess 1 year ago Restore.php 7 months ago index.html 1 year ago restore.template.php 7 months ago web.config 1 year ago
Restore.php
180 lines
1 <?php
2
3 namespace JetBackup\Restore;
4
5 use Exception;
6 use JetBackup\Cron\Task\Task;
7 use JetBackup\Exception\ExecutionTimeException;
8 use JetBackup\Exception\RestoreException;
9 use JetBackup\Factory;
10 use JetBackup\IO\Lock;
11 use JetBackup\JetBackup;
12 use JetBackup\Queue\Queue;
13 use JetBackup\Queue\QueueItem;
14 use JetBackup\Wordpress\Wordpress;
15 use Throwable;
16
17 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
18
19 class Restore {
20
21 const ACTION_RESTORE = 'restore';
22 const ACTION_STATUS = 'status';
23 const ACTION_CANCEL = 'cancel';
24 const ACTION_COMPLETED = 'completed';
25
26 private QueueItem $_queue;
27 private Task $_task;
28
29 public function __construct(QueueItem $queueItem) {
30 $this->_queue = $queueItem;
31 $this->_task = new \JetBackup\Cron\Task\Restore();
32 $this->_task->setQueueItem($queueItem);
33 $this->_task->setExecutionTimeLimit(30);
34 $this->_task->setExecutionTimeDie(false);
35 }
36
37 public function execute($action) {
38 switch ($action) {
39 case self::ACTION_CANCEL: $this->_actionCancel();
40 case self::ACTION_COMPLETED: $this->_actionCompleted();
41 case self::ACTION_STATUS: $this->_actionStatus();
42 case self::ACTION_RESTORE: $this->_actionRestore();
43 }
44 }
45
46 private static function _getRestorePath() : string {
47 return Factory::getSettingsRestore()->isRestoreAlternatePathEnabled() ? WP_ROOT . JetBackup::SEP . JetBackup::CRON_PUBLIC_URL : WP_ROOT;
48 }
49
50 private static function _getRestoreFileName(): string {
51 if (!isset($_SERVER['SCRIPT_FILENAME'])) return '';
52 $script = Wordpress::getUnslash($_SERVER['SCRIPT_FILENAME']);
53 $script = Wordpress::sanitizeTextField($script);
54 return basename($script);
55 }
56
57
58 private static function _getRestoreFile() : string {
59 return self::_getRestorePath() . JetBackup::SEP . self::_getRestoreFileName();
60 }
61
62 private static function _getLockFile() : string {
63 return self::_getRestoreFile() . '.lock';
64 }
65
66 private function _actionRestore() {
67
68 if (!Lock::LockFile(self::_getLockFile())) {
69 self::_output(true, 'Already running...', [
70 'status' => $this->_queue->getStatus(),
71 ]);
72 }
73
74 if($this->_queue->getStatus() < Queue::STATUS_DONE) {
75 try {
76 $this->_task->execute();
77 } catch(ExecutionTimeException $e) {
78 self::_output(true, 'Execution time reached', [ 'status' => $this->_queue->getStatus() ]);
79 } catch(Exception $e) {
80 self::_output(false, "Error: " . $e->getMessage());
81 } catch(Throwable $e) {
82 self::_output(false, "Fatal Error: " . $e->getMessage() . "\nTrace:\n" . $e->getTraceAsString());
83 }
84 }
85
86 self::_output(true, 'Completed', [
87 'status' => $this->_queue->getStatus(),
88 ]);
89 }
90
91 private static function _selfDelete() {
92 @unlink(self::_getRestoreFile());
93 @unlink(self::_getLockFile());
94 }
95
96 private function _actionCompleted() {
97
98 if($this->_queue->getStatus() < Queue::STATUS_DONE)
99 self::_output(false, 'Restore haven\'t completed yet');
100
101 self::_selfDelete();
102
103 self::_output(true, 'Completed Successfully');
104 }
105
106 private function _actionCancel() {
107
108 $this->_queue->updateStatus(Queue::STATUS_ABORTED);
109 $this->_queue->updateProgress('Restore aborted by the user', QueueItem::PROGRESS_LAST_STEP);
110
111 self::_selfDelete();
112
113 self::_output(true, 'Cancelled Successfully');
114 }
115
116 // In _actionStatus()
117
118 private function _actionStatus() {
119 $log_entries = [];
120 $log_file = $this->_task->getLogFile();
121
122 $chunk = '';
123 $cursor_in = isset($_POST['cursor']) ? (int)$_POST['cursor'] : -1; // -1 = first call (tail last N lines)
124 $next_cursor = 0;
125 $reset = false;
126
127 if (is_readable($log_file)) {
128 $size = filesize($log_file);
129 $next_cursor = $size;
130
131 // rotation/shrink detection
132 if ($cursor_in > $size) {
133 $cursor_in = -1;
134 $reset = true;
135 }
136
137 $fh = fopen($log_file, 'rb');
138 if ($fh) {
139 if ($cursor_in < 0) {
140 // first call: tail last ~4KB or last 300 lines (whichever is smaller)
141 $tailBytes = 4096;
142 if ($size > $tailBytes) fseek($fh, -$tailBytes, SEEK_END);
143 $raw = stream_get_contents($fh);
144 // keep only last 300 lines to avoid blasting the client
145 $lines = explode("\n", $raw);
146 if (count($lines) > 300) {
147 $lines = array_slice($lines, -300);
148 }
149 $chunk = implode("\n", $lines);
150 } else {
151 // incremental read
152 fseek($fh, $cursor_in, SEEK_SET);
153 // clamp max chunk (e.g., 64KB) to keep responses light
154 $chunk = stream_get_contents($fh, 64 * 1024);
155 }
156 fclose($fh);
157 }
158 }
159
160 $progress = $this->_queue->getProgress();
161
162 self::_output(true, '', [
163 'status' => $this->_queue->getStatus(),
164 'progress' => $progress->getDisplay(),
165 'log_chunk' => $chunk, // string (may be empty)
166 'cursor' => $next_cursor, // new cursor for next request
167 'reset' => $reset, // client should clear if true
168 ]);
169 }
170
171
172 private static function _output($success, $message, $data=[]) {
173 die(json_encode([
174 'success' => $success ? 1 : 0,
175 'message' => $message,
176 'data' => $data,
177 ]));
178 }
179
180 }