PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / 3.1.13.4
JetBackup – Backup, Restore & Migrate v3.1.13.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 9 months ago Backup.php 9 months ago Download.php 9 months ago DownloadBackupLog.php 9 months ago Export.php 9 months ago Extract.php 9 months ago PreRestore.php 9 months ago Reindex.php 9 months ago Restore.php 9 months ago RetentionCleanup.php 9 months ago System.php 9 months ago Task.php 9 months ago index.html 9 months ago web.config 9 months ago
PreRestore.php
371 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
184 if($this->_snapshot->getEngine() == Engine::ENGINE_JB) return;
185
186 $this->getLogController()->logMessage('[ _extract ]');
187 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
188 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
189
190 $this->getQueueItem()->updateStatus(Queue::STATUS_RESTORE_EXTRACT);
191 $this->getQueueItem()->updateProgress('Extracting backup items');
192
193 if($this->_snapshot->getEngine() == Engine::ENGINE_SGB) {
194
195 // SGB snapshot has only 1 item
196 $item = $this->_snapshot->getItems()[0];
197
198 $path = $this->getQueueItem()->getWorkspace() . JetBackup::SEP . $item->getPath();
199
200 try {
201 $extractor = new Extractor($path, $this->getQueueItem()->getWorkspace());
202 $extractor->setLogController($this->getLogController());
203 $extractor->extract(function() {
204 $this->checkExecutionTime();
205 });
206 } catch(SGBExtractorException $e) {
207 throw new DownloaderException($e->getMessage());
208 }
209
210 unlink($path);
211
212 } else {
213
214 $this->func(function () {
215 $this->_snapshot->extract($this->getQueueItem()->getWorkspace(), $this->getLogController(), function(string $type, string $action, int $total, int $read) {
216
217 $progress = $this->getQueueItem()->getProgress();
218 $progress->setMessage($type); // gzip / archive
219 $progress->setSubMessage($action); // decompress / extract
220 $progress->setTotalSubItems($total);
221 $progress->setCurrentSubItem($read);
222 $this->getQueueItem()->save();
223
224 // Call checkExecutionTime, passing the desired variables
225 $this->checkExecutionTime(function () use ($type, $action, $total, $read) {
226 $progress = $this->getQueueItem()->getProgress();
227 $progress->setMessage($type);
228 $progress->setSubMessage('Waiting for next cron');
229 $progress->setTotalSubItems($total);
230 $progress->setCurrentSubItem($read);
231 $this->getQueueItem()->save();
232 });
233
234 }, $this->_queue_item_restore->getExcludes(), $this->_queue_item_restore->getIncludes());
235 }, [], 'snapshot_extract');
236
237 // done extract, reset sub process bar
238 $this->getQueueItem()->getProgress()->resetSub();
239 $this->getQueueItem()->save();
240
241 }
242 }
243
244 /**
245 * @return void
246 * @throws RestoreException
247 * @throws DBException
248 * @throws IOException
249 * @throws InvalidArgumentException
250 * @throws Exception
251 */
252 public function _download():Snapshot {
253
254 $this->getLogController()->logMessage('[ _download ]');
255 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
256 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
257
258 $this->getQueueItem()->updateStatus(Queue::STATUS_RESTORE_DOWNLOAD);
259 if($this->_queue_item_restore->getSnapshotId()) {
260 $this->getLogController()->logMessage('[ _download ] Downloading backup');
261 $this->getQueueItem()->updateProgress('Downloading backup');
262
263 $snapshot = new Snapshot($this->_queue_item_restore->getSnapshotId());
264
265 $this->getLogController()->logDebug('[ _download ] getOptions bitwise value: ' . $this->_queue_item_restore->getOptions());
266 $this->getLogController()->logDebug('[ _download ] getOptions decimal value: ' . decbin($this->_queue_item_restore->getOptions()));
267
268 if($snapshot->getEngine() == Engine::ENGINE_JB) return $snapshot;
269
270 $items_excluded = [];
271
272 if (($this->_queue_item_restore->getOptions() & QueueItemRestore::OPTION_RESTORE_FILES_SKIP)) {
273 $this->getLogController()->logDebug('[ _download ] Skipping Homedir');
274 $items_excluded[] = BackupJob::BACKUP_ACCOUNT_CONTAINS_HOMEDIR;
275 }
276
277 if (($this->_queue_item_restore->getOptions() & QueueItemRestore::OPTION_RESTORE_DATABASE_SKIP)) {
278 $this->getLogController()->logDebug('[ _download ] Skipping Database');
279 $items_excluded[] = BackupJob::BACKUP_ACCOUNT_CONTAINS_DATABASE;
280 }
281
282 $this->getLogController()->logDebug('[ _download ] Excluded items: ' . print_r($items_excluded, true));
283
284 $download = new SnapshotDownload($snapshot, $this->getQueueItem()->getWorkspace());
285 $download->setLogController($this->getLogController());
286 $download->setQueueItem($this->getQueueItem());
287 $download->setTask($this);
288 $download->setExcludedItems($items_excluded);
289 $download->setExcludedDatabases($this->_queue_item_restore->getExcludedDatabases());
290 $download->setIncludedDatabases($this->_queue_item_restore->getIncludedDatabases());
291 $download->downloadAll();
292
293 // done downloading, reset sub process bar
294 $this->getQueueItem()->getProgress()->resetSub();
295 $this->getQueueItem()->save();
296
297 } elseif($this->_queue_item_restore->getSnapshotPath()) {
298 $this->getLogController()->logMessage('[ _download ] Extracting backup from path');
299 $this->getQueueItem()->updateProgress('Extracting backup from path');
300 $snapshot = $this->_extractBackupFile();
301 }
302
303 return $snapshot;
304 }
305
306 /**
307 * @throws RestoreException
308 * @throws Exception
309 */
310 private function _extractBackupFile():Snapshot {
311 $path = $this->_queue_item_restore->getSnapshotPath();
312 if(!file_exists($path)) throw new RestoreException("The provided backup path doesn't exists");
313
314 if(Archive::isGzCompressed($path)) {
315 $this->getLogController()->logMessage('[ _extractBackupFile ] Decompressing GZIP');
316 $this->func(['\JetBackup\Archive\Gzip', 'decompress'], [$path, ResumableTask::PARAMS_EXECUTION_TIME]);
317 $path = substr($path, 0, -3); // remove .gz from name
318 }
319
320 if(!Archive::isTar($path)) throw new RestoreException("Invalid backup file provided, Should be tar.gz/tar.gz file");
321
322 $this->func(function($path) {
323 $archive = new Archive($path);
324 $archive->setExtractFileCallback(function($type,$action,$total,$read) {
325 $progress = $this->getQueueItem()->getProgress();
326 $progress->setMessage($type);
327 $progress->setSubMessage($action);
328 $progress->setTotalSubItems($total);
329 $progress->setCurrentSubItem($read);
330 $this->getQueueItem()->save();
331
332 $this->checkExecutionTime(function () use ($type, $action, $total, $read) {
333 $progress = $this->getQueueItem()->getProgress();
334 $progress->setMessage($type);
335 $progress->setSubMessage('Waiting for next cron');
336 $progress->setTotalSubItems($total);
337 $progress->setCurrentSubItem($read);
338 $this->getQueueItem()->save();
339 });
340
341 });
342 $archive->setExcludeCallback(function($path, $is_dir) {
343 $excludes = $this->_queue_item_restore->getExcludes();
344 foreach($excludes as $exclude) if(fnmatch($exclude, $path) || ($is_dir && str_ends_with($exclude, '/') && fnmatch(substr($exclude, 0, -1), $path))) return true;
345 return false;
346 });
347 $archive->setLogController($this->getLogController());
348 $archive->extract($this->getQueueItem()->getWorkspace());
349 unlink($path);
350 }, [$path], 'extract');
351
352 // done extract, reset sub process bar
353 $this->getQueueItem()->getProgress()->resetSub();
354 $this->getQueueItem()->save();
355
356 $meta_file = sprintf(Snapshot::META_FILEPATH, $this->getQueueItem()->getWorkspace());
357 if(!file_exists($meta_file)) throw new RestoreException("The provided backup doesn't contains meta file");
358
359 $this->getLogController()->logDebug("[ _extractBackupFile ] Importing meta file $meta_file");
360 $snapshot = new Snapshot();
361
362
363 try {
364 $snapshot->importMeta($meta_file, true);
365 } catch(SnapshotMetaException $e) {
366 throw new RestoreException("Can't use the provided backup file. Error: " . $e->getMessage());
367 }
368
369 return $snapshot;
370 }
371 }