PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 3.1.11.1
JetBackup – Backup, Restore & Migrate v3.1.11.1
3.1.23.6 3.1.23.5 3.1.23.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 / src / JetBackup / Cron / Task / PreRestore.php
backup / src / JetBackup / Cron / Task Last commit date
.htaccess 1 year ago Backup.php 1 year ago Download.php 1 year ago DownloadBackupLog.php 1 year ago Export.php 1 year ago Extract.php 1 year ago PreRestore.php 1 year ago Reindex.php 1 year ago Restore.php 1 year ago RetentionCleanup.php 1 year ago System.php 1 year ago Task.php 1 year ago index.html 1 year ago web.config 1 year ago
PreRestore.php
370 lines
1 <?php
2
3 namespace JetBackup\Cron\Task;
4
5 use Exception;
6 use JetBackup\Archive\Archive;
7 use JetBackup\BackupJob\BackupJob;
8 use JetBackup\Data\Engine;
9 use JetBackup\Destination\Destination;
10 use JetBackup\Entities\Util;
11 use JetBackup\Exception\DBException;
12 use JetBackup\Exception\DownloaderException;
13 use JetBackup\Exception\ExecutionTimeException;
14 use JetBackup\Exception\RestoreException;
15 use JetBackup\Exception\SGBExtractorException;
16 use JetBackup\Exception\SnapshotMetaException;
17 use JetBackup\Exception\TaskException;
18 use JetBackup\Factory;
19 use JetBackup\JetBackup;
20 use JetBackup\License\License;
21 use JetBackup\Queue\Queue;
22 use JetBackup\Queue\QueueItem;
23 use JetBackup\Queue\QueueItemRestore;
24 use JetBackup\ResumableTask\ResumableTask;
25 use JetBackup\SGB\Extractor;
26 use JetBackup\Snapshot\Snapshot;
27 use JetBackup\Snapshot\SnapshotDownload;
28 use JetBackup\Wordpress\Helper;
29 use JetBackup\Wordpress\Wordpress;
30 use SleekDB\Exceptions\InvalidArgumentException;
31 use SleekDB\Exceptions\IOException;
32
33 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
34
35 class PreRestore extends Task {
36
37 const LOG_FILENAME = 'pre_restore';
38
39 const RESTORE_FILE_NAME = 'jetbackup.restore';
40
41 private QueueItemRestore $_queue_item_restore;
42 public ?Snapshot $_snapshot=null;
43
44 public function __construct() {
45 parent::__construct(self::LOG_FILENAME);
46 }
47
48 /**
49 * @return void
50 * @throws DBException
51 * @throws IOException
52 * @throws InvalidArgumentException
53 * @throws TaskException
54 */
55 public function execute():void {
56 parent::execute();
57
58 if($this->getQueueItem()->getStatus() >= Queue::STATUS_RESTORE_WAITING_FOR_RESTORE) {
59
60 // If external restore hasn't initiated within 24 hours we need to abort the restore process
61 if($this->getQueueItem()->getStatusTime() < (time() - (60 * 60 * 24))) {
62 $this->getLogController()->logError("External restore hasn't initiated within the last 24 hours, Aborting the restore");
63 $this->getQueueItem()->updateStatus(Queue::STATUS_ABORTED);
64 $this->getQueueItem()->updateProgress('Restore Aborted!', QueueItem::PROGRESS_LAST_STEP);
65 } else {
66 $this->getLogController()->logMessage('Waiting for external restore');
67 }
68
69 return;
70 }
71
72 $this->_queue_item_restore = $this->getQueueItem()->getItemData();
73
74 if($this->_queue_item_restore->getSnapshotId()) {
75
76 $snapshot = new Snapshot($this->_queue_item_restore->getSnapshotId());
77 $destination = new Destination($snapshot->getDestinationId());
78
79 if(!License::isValid() &&
80 !in_array($destination->getType(), Destination::LICENSE_EXCLUDED) &&
81 $snapshot->getEngine() != Engine::ENGINE_JB) {
82
83 $this->getLogController()->logError("You can't restore from {$destination->getType()} destination without a license");
84 $this->getQueueItem()->updateStatus(Queue::STATUS_ABORTED);
85 $this->getQueueItem()->updateProgress('Restore Aborted!', QueueItem::PROGRESS_LAST_STEP);
86 return;
87 }
88 }
89
90 if($this->getQueueItem()->getStatus() == Queue::STATUS_PENDING) {
91 $this->getLogController()->logMessage("Starting restore");
92
93 $this->getQueueItem()->getProgress()->setTotalItems( count(Queue::STATUS_PRE_RESTORE_NAMES) + 3);
94 $this->getQueueItem()->save();
95
96 $this->getQueueItem()->updateProgress('Starting restore');
97 } else if($this->getQueueItem()->getStatus() > Queue::STATUS_PENDING) {
98 $this->getLogController()->logMessage('Resumed Restore');
99 }
100
101 try {
102
103 if(!$this->_queue_item_restore->getSnapshotId() && !$this->_queue_item_restore->getSnapshotPath())
104 throw new RestoreException("No snapshot id or path provided");
105 $this->getLogController()->logDebug('Item data: ' . print_r($this->_queue_item_restore, 1));
106 $this->_snapshot = $this->func([$this, '_download']);
107 $this->func([$this, '_extract']);
108 $this->func([$this, '_build_url']);
109
110 if($this->getQueueItem()->getStatus() < Queue::STATUS_DONE) $this->getQueueItem()->updateStatus(Queue::STATUS_RESTORE_WAITING_FOR_RESTORE);
111 $this->getLogController()->logMessage('Completed!');
112 } catch(Exception $e) {
113 $this->getQueueItem()->updateStatus(Queue::STATUS_FAILED);
114 $this->getLogController()->logError($e->getMessage());
115 $this->getLogController()->logMessage('Failed!');
116 }
117
118 $this->getQueueItem()->updateProgress($this->getQueueItem()->getStatus() == Queue::STATUS_RESTORE_WAITING_FOR_RESTORE ? 'Pre Restore Completed!' : 'Pre Restore Failed!', QueueItem::PROGRESS_LAST_STEP);
119 $this->getLogController()->logMessage('Total time: ' . $this->getExecutionTimeElapsed());
120 }
121
122 public static function findPublicRestoreFiles() : array {
123 $basePath = Factory::getWPHelper()->getRestoreFileLocation() . JetBackup::SEP . self::RESTORE_FILE_NAME . '.*';
124 if (defined('GLOB_BRACE')) return glob($basePath . '.{php,php.lock}', \GLOB_BRACE);
125 $phpFiles = glob($basePath . '.php');
126 $phpLockFiles = glob($basePath . '.php.lock');
127 return array_merge($phpFiles ?: [], $phpLockFiles ?: []);
128 }
129
130 /**
131 * @return void
132 * @throws IOException
133 * @throws InvalidArgumentException
134 * @throws RestoreException
135 */
136 public function _build_url() {
137
138 $this->getLogController()->logMessage('[ _build_url ]');
139 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
140 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
141
142 $this->getQueueItem()->updateStatus(Queue::STATUS_RESTORE_BUILD_URL);
143 $this->getQueueItem()->updateProgress('Building restore URL');
144
145 $template_file = JetBackup::SRC_PATH . JetBackup::SEP . 'Restore' . JetBackup::SEP . 'restore.template.php';
146 if(!file_exists($template_file)) throw new RestoreException("Restore template file not found");
147
148 $public_dir = rtrim(Factory::getWPHelper()->getWordPressHomedir(), JetBackup::SEP);
149 foreach (self::findPublicRestoreFiles() as $file) @unlink($file);
150
151 $content = "<?php define('__JETBACKUP_RESTORE__', true); ?>\n";
152 $content .= "<?php define('WP_ROOT', '$public_dir'); ?>\n";
153 $content .= "<?php define('PUBLIC_PATH', '" . (Factory::getSettingsRestore()->isRestoreAlternatePathEnabled() ? str_repeat('../', substr_count(JetBackup::CRON_PUBLIC_URL, '/')) : '') . "'); ?>\n";
154 $content .= file_get_contents($template_file);
155
156 $restore_file_name = self::RESTORE_FILE_NAME . '.' . Util::generateRandomString(24) . '.php';
157 $restore_file = Factory::getWPHelper()->getRestoreFileLocation() . JetBackup::SEP . $restore_file_name;
158
159 if(!file_put_contents($restore_file, $content)) throw new RestoreException("Failed creating restore file $restore_file");
160
161 // Dev Remark
162 //symlink('wp-content/plugins/backup/src/JetBackup/Restore/restore.template.php', $restore_file);
163
164 $alternate_path = Factory::getSettingsRestore()->isRestoreAlternatePathEnabled() ? JetBackup::CRON_PUBLIC_URL : '';
165
166 $url = Wordpress::getSiteURL() . $alternate_path . '/' . $restore_file_name . '?id=' . $this->getQueueItem()->getUniqueId();
167
168 $this->getLogController()->logMessage('Restore URL: ' . $url);
169
170 $this->_queue_item_restore->setRestoreURL($url);
171 $this->getQueueItem()->save();
172 }
173
174 /**
175 * @return void
176 * @throws DBException
177 * @throws DownloaderException
178 * @throws ExecutionTimeException
179 * @throws IOException
180 * @throws InvalidArgumentException
181 */
182 public function _extract() {
183 if($this->_snapshot->getEngine() == Engine::ENGINE_JB) return;
184
185 $this->getLogController()->logMessage('[ _extract ]');
186 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
187 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
188
189 $this->getQueueItem()->updateStatus(Queue::STATUS_RESTORE_EXTRACT);
190 $this->getQueueItem()->updateProgress('Extracting backup items');
191
192 if($this->_snapshot->getEngine() == Engine::ENGINE_SGB) {
193
194 // SGB snapshot has only 1 item
195 $item = $this->_snapshot->getItems()[0];
196
197 $path = $this->getQueueItem()->getWorkspace() . JetBackup::SEP . $item->getPath();
198
199 try {
200 $extractor = new Extractor($path, $this->getQueueItem()->getWorkspace());
201 $extractor->setLogController($this->getLogController());
202 $extractor->extract(function() {
203 $this->checkExecutionTime();
204 });
205 } catch(SGBExtractorException $e) {
206 throw new DownloaderException($e->getMessage());
207 }
208
209 unlink($path);
210
211 } else {
212
213 $this->func(function () {
214 $this->_snapshot->extract($this->getQueueItem()->getWorkspace(), $this->getLogController(), function(string $type, string $action, int $total, int $read) {
215
216 $progress = $this->getQueueItem()->getProgress();
217 $progress->setMessage($type); // gzip / archive
218 $progress->setSubMessage($action); // decompress / extract
219 $progress->setTotalSubItems($total);
220 $progress->setCurrentSubItem($read);
221 $this->getQueueItem()->save();
222
223 // Call checkExecutionTime, passing the desired variables
224 $this->checkExecutionTime(function () use ($type, $action, $total, $read) {
225 $progress = $this->getQueueItem()->getProgress();
226 $progress->setMessage($type);
227 $progress->setSubMessage('Waiting for next cron');
228 $progress->setTotalSubItems($total);
229 $progress->setCurrentSubItem($read);
230 $this->getQueueItem()->save();
231 });
232
233 }, $this->_queue_item_restore->getExcludes(), $this->_queue_item_restore->getIncludes());
234 }, [], 'snapshot_extract');
235
236 // done extract, reset sub process bar
237 $this->getQueueItem()->getProgress()->resetSub();
238 $this->getQueueItem()->save();
239
240 }
241 }
242
243 /**
244 * @return void
245 * @throws RestoreException
246 * @throws DBException
247 * @throws IOException
248 * @throws InvalidArgumentException
249 * @throws Exception
250 */
251 public function _download():Snapshot {
252
253 $this->getLogController()->logMessage('[ _download ]');
254 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
255 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
256
257 $this->getQueueItem()->updateStatus(Queue::STATUS_RESTORE_DOWNLOAD);
258 if($this->_queue_item_restore->getSnapshotId()) {
259 $this->getLogController()->logMessage('[ _download ] Downloading backup');
260 $this->getQueueItem()->updateProgress('Downloading backup');
261
262 $snapshot = new Snapshot($this->_queue_item_restore->getSnapshotId());
263
264 $this->getLogController()->logDebug('[ _download ] getOptions bitwise value: ' . $this->_queue_item_restore->getOptions());
265 $this->getLogController()->logDebug('[ _download ] getOptions decimal value: ' . decbin($this->_queue_item_restore->getOptions()));
266
267 if($snapshot->getEngine() == Engine::ENGINE_JB) return $snapshot;
268
269 $items_excluded = [];
270
271 if (($this->_queue_item_restore->getOptions() & QueueItemRestore::OPTION_RESTORE_FILES_SKIP)) {
272 $this->getLogController()->logDebug('[ _download ] Skipping Homedir');
273 $items_excluded[] = BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR;
274 }
275
276 if (($this->_queue_item_restore->getOptions() & QueueItemRestore::OPTION_RESTORE_DATABASE_SKIP)) {
277 $this->getLogController()->logDebug('[ _download ] Skipping Database');
278 $items_excluded[] = BackupJob::BACKUP_ACCOUNT_CONTAINS_DATABASE;
279 }
280
281 $this->getLogController()->logDebug('[ _download ] Excluded items: ' . print_r($items_excluded, true));
282
283 $download = new SnapshotDownload($snapshot, $this->getQueueItem()->getWorkspace());
284 $download->setLogController($this->getLogController());
285 $download->setQueueItem($this->getQueueItem());
286 $download->setTask($this);
287 $download->setExcludedItems($items_excluded);
288 $download->setExcludedDatabases($this->_queue_item_restore->getExcludedDatabases());
289 $download->setIncludedDatabases($this->_queue_item_restore->getIncludedDatabases());
290 $download->downloadAll();
291
292 // done downloading, reset sub process bar
293 $this->getQueueItem()->getProgress()->resetSub();
294 $this->getQueueItem()->save();
295
296 } elseif($this->_queue_item_restore->getSnapshotPath()) {
297 $this->getLogController()->logMessage('[ _download ] Extracting backup from path');
298 $this->getQueueItem()->updateProgress('Extracting backup from path');
299 $snapshot = $this->_extractBackupFile();
300 }
301
302 return $snapshot;
303 }
304
305 /**
306 * @throws RestoreException
307 * @throws Exception
308 */
309 private function _extractBackupFile():Snapshot {
310 $path = $this->_queue_item_restore->getSnapshotPath();
311 if(!file_exists($path)) throw new RestoreException("The provided backup path doesn't exists");
312
313 if(Archive::isGzCompressed($path)) {
314 $this->getLogController()->logMessage('[ _extractBackupFile ] Decompressing GZIP');
315 $this->func(['\JetBackup\Archive\Gzip', 'decompress'], [$path, ResumableTask::PARAMS_EXECUTION_TIME]);
316 $path = substr($path, 0, -3); // remove .gz from name
317 }
318
319 if(!Archive::isTar($path)) throw new RestoreException("Invalid backup file provided, Should be tar.gz/tar.gz file");
320
321 $this->func(function($path) {
322 $archive = new Archive($path);
323 $archive->setExtractFileCallback(function($type,$action,$total,$read) {
324 $progress = $this->getQueueItem()->getProgress();
325 $progress->setMessage($type);
326 $progress->setSubMessage($action);
327 $progress->setTotalSubItems($total);
328 $progress->setCurrentSubItem($read);
329 $this->getQueueItem()->save();
330
331 $this->checkExecutionTime(function () use ($type, $action, $total, $read) {
332 $progress = $this->getQueueItem()->getProgress();
333 $progress->setMessage($type);
334 $progress->setSubMessage('Waiting for next cron');
335 $progress->setTotalSubItems($total);
336 $progress->setCurrentSubItem($read);
337 $this->getQueueItem()->save();
338 });
339
340 });
341 $archive->setExcludeCallback(function($path, $is_dir) {
342 $excludes = $this->_queue_item_restore->getExcludes();
343 foreach($excludes as $exclude) if(fnmatch($exclude, $path) || ($is_dir && str_ends_with($exclude, '/') && fnmatch(substr($exclude, 0, -1), $path))) return true;
344 return false;
345 });
346 $archive->setLogController($this->getLogController());
347 $archive->extract($this->getQueueItem()->getWorkspace());
348 unlink($path);
349 }, [$path], 'extract');
350
351 // done extract, reset sub process bar
352 $this->getQueueItem()->getProgress()->resetSub();
353 $this->getQueueItem()->save();
354
355 $meta_file = sprintf(Snapshot::META_FILEPATH, $this->getQueueItem()->getWorkspace());
356 if(!file_exists($meta_file)) throw new RestoreException("The provided backup doesn't contains meta file");
357
358 $this->getLogController()->logDebug("[ _extractBackupFile ] Importing meta file $meta_file");
359 $snapshot = new Snapshot();
360
361
362 try {
363 $snapshot->importMeta($meta_file, true);
364 } catch(SnapshotMetaException $e) {
365 throw new RestoreException("Can't use the provided backup file. Error: " . $e->getMessage());
366 }
367
368 return $snapshot;
369 }
370 }