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 / System.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
System.php
529 lines
1 <?php
2
3 namespace JetBackup\Cron\Task;
4
5 use Exception;
6 use JetBackup\Alert\Alert;
7 use JetBackup\BackupJob\BackupJob;
8 use JetBackup\Download\Download;
9 use JetBackup\Entities\Util;
10 use JetBackup\Exception\DBException;
11 use JetBackup\Exception\HttpRequestException;
12 use JetBackup\Exception\IOException;
13 use JetBackup\Exception\JetBackupLinuxException;
14 use JetBackup\Exception\LicenseException;
15 use JetBackup\Exception\NotificationException;
16 use JetBackup\Exception\QueueException;
17 use JetBackup\Factory;
18 use JetBackup\JetBackup;
19 use JetBackup\JetBackupLinux\JetBackupLinux;
20 use JetBackup\License\License;
21 use JetBackup\Notification\Notification;
22 use JetBackup\Queue\Queue;
23 use JetBackup\Queue\QueueItem;
24 use JetBackup\Queue\QueueItemSystem;
25 use JetBackup\Upload\Upload;
26 use JetBackup\Web\JetHttp;
27 use JetBackup\Wordpress\Helper;
28 use JetBackup\Wordpress\Wordpress;
29 use SleekDB\Exceptions\InvalidArgumentException;
30
31 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
32
33 class System extends Task {
34
35 const LOG_FILENAME = 'system';
36
37 const CHECKSUM_URL = "https://api.wordpress.org/core/checksums/1.0/?version=%s&locale=%s";
38
39 const TYPE_HOURLY = 1;
40 const TYPE_DAILY = 2;
41
42 const TYPE_NAMES = [
43 self::TYPE_HOURLY => 'Hourly',
44 self::TYPE_DAILY => 'Daily',
45 ];
46
47 const HOURLY_INTERVAL = 14400; // 4 hours
48 const DAILY_INTERVAL = 86400; // 24 hours
49
50 const TEMP_FILES_TTL = 86400;
51 const VALIDATE_CHECKSUMS_EXCLUDES = [ 'wp-content/themes/*', 'wp-content/plugins/*' ];
52 private QueueItemSystem $_queue_item_system;
53
54 public function __construct() {
55 parent::__construct(self::LOG_FILENAME);
56 }
57
58 public function execute():void {
59 parent::execute();
60
61 $this->_queue_item_system = $this->getQueueItem()->getItemData();
62
63 if($this->getQueueItem()->getStatus() == Queue::STATUS_PENDING) {
64 $this->getLogController()->logMessage('Starting ' . self::TYPE_NAMES[$this->_queue_item_system->getType()] . ' System Tasks');
65
66 $this->getQueueItem()->getProgress()->setTotalItems($this->_queue_item_system->getType() == self::TYPE_DAILY ? 9 : 6);
67 $this->getQueueItem()->save();
68
69 $this->getQueueItem()->updateProgress('Starting ' . self::TYPE_NAMES[$this->_queue_item_system->getType()] . ' System Tasks');
70 } else if($this->getQueueItem()->getStatus() > Queue::STATUS_PENDING) {
71 $this->getLogController()->logMessage('Resumed ' . self::TYPE_NAMES[$this->_queue_item_system->getType()] . ' System Tasks');
72 }
73
74 try {
75 switch($this->_queue_item_system->getType()) {
76 case self::TYPE_DAILY:
77 $this->func([$this, '_checkLicense']);
78 $this->func([$this, '_backupJobMonitor']);
79 $this->func([$this, '_databaseCleanup']);
80 $this->func([$this, '_retentionCleanup']);
81 $this->func([$this, '_uploadCleanup']);
82 $this->func([$this, '_systemCleanup']);
83 $this->func([$this, '_logsCleanup']);
84 $this->func([$this, '_validateChecksums']);
85 $this->func([$this, '_processDailyAlerts']);
86 $this->func([$this, '_indexJBBackups']);
87 break;
88
89 case self::TYPE_HOURLY:
90 $this->func([$this, '_checkLicense']);
91 $this->func([$this, '_databaseCleanup']);
92 $this->func([$this, '_retentionCleanup']);
93 $this->func([$this, '_downloadsCleanup']);
94 $this->func([$this, '_indexJBBackups']);
95 break;
96 }
97
98 if($this->getQueueItem()->getStatus() < Queue::STATUS_DONE && !$this->getQueueItem()->getErrors()) $this->getQueueItem()->updateStatus(Queue::STATUS_DONE);
99 else $this->getQueueItem()->updateStatus(Queue::STATUS_PARTIALLY);
100 $this->getLogController()->logMessage('Completed!');
101 } catch(\Exception $e) {
102 $this->getQueueItem()->updateStatus(Queue::STATUS_FAILED);
103 $this->getLogController()->logError($e->getMessage());
104 $this->getLogController()->logMessage('Failed!');
105 }
106
107 $this->getQueueItem()->updateProgress(
108 $this->getQueueItem()->getStatus() == Queue::STATUS_DONE
109 ? 'System Tasks Completed!'
110 : ($this->getQueueItem()->getStatus() == Queue::STATUS_PARTIALLY
111 ? 'Completed with errors (see logs)'
112 : 'System Tasks Failed!'),
113 QueueItem::PROGRESS_LAST_STEP
114 );
115
116 $this->getLogController()->logMessage('Total time: ' . $this->getExecutionTimeElapsed());
117 }
118
119 /**
120 * @return void
121 * @throws IOException
122 * @throws InvalidArgumentException
123 * @throws QueueException
124 * @throws \SleekDB\Exceptions\IOException
125 */
126 public static function addToQueue() {
127
128 $system = new QueueItemSystem();
129
130 $queue_item = QueueItem::prepare();
131 $queue_item->setType(Queue::QUEUE_TYPE_SYSTEM);
132 $daily_last_run = Factory::getConfig()->getSystemCronDailyLastRun();
133 $hourly_last_run = Factory::getConfig()->getSystemCronHourlyLastRun();
134
135 // Check if we need to run daily tasks
136 if(!$daily_last_run || $daily_last_run < (time() - self::DAILY_INTERVAL)) {
137 $system->setType(self::TYPE_DAILY);
138 Factory::getConfig()->setSystemCronDailyLastRun();
139 }
140 // Only if we don't need to run daily tasks, check if we need to run hourly tasks
141 elseif (!$hourly_last_run || $hourly_last_run < (time() - self::HOURLY_INTERVAL)) {
142 $system->setType(self::TYPE_HOURLY);
143 } else return;
144 Factory::getConfig()->setSystemCronHourlyLastRun();
145
146 $queue_item->setItemData($system);
147 Queue::addToQueue($queue_item);
148
149 Factory::getConfig()->save();
150 }
151
152 public function _uploadCleanup() {
153 $this->getLogController()->logMessage("\t [_retentionCleanup]");
154 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
155 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
156
157 $this->getQueueItem()->updateStatus(Queue::STATUS_SYSTEM_UPLOAD_CLEANUP);
158 $this->getQueueItem()->updateProgress('Uploads Cleanup');
159
160 $list = Upload::query()
161 ->where([Upload::CREATED, '<', time() - (60 * 60 * 24)])
162 ->getQuery()
163 ->fetch();
164
165 foreach($list as $upload_details) {
166 $upload = new Upload($upload_details[JetBackup::ID_FIELD]);
167 if(!$upload->getId()) continue;
168
169 $location = dirname($upload->getFileLocation());
170
171 if($location && is_dir($location)) Util::rm($location);
172 $upload->delete();
173 }
174 }
175
176 /**
177 * @throws \SleekDB\Exceptions\IOException
178 * @throws InvalidArgumentException
179 */
180 public function _databaseCleanup() {
181
182 $this->getLogController()->logMessage("\t [_databaseCleanup]");
183 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
184 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
185 $this->getQueueItem()->updateStatus(Queue::STATUS_SYSTEM_DB_CLEANUP);
186 $this->getQueueItem()->updateProgress('Database Cleanup');
187
188 if($ttl = Factory::getSettingsMaintenance()->getQueueItemsTTL()) {
189 QueueItem::query()
190 ->where([QueueItem::STATUS, '>=', Queue::STATUS_DONE])
191 ->where([QueueItem::CREATED, '<', (time() - ($ttl * 3600))])
192 ->getQuery()
193 ->delete();
194 }
195
196 if($ttl = Factory::getSettingsMaintenance()->getAlertsTTL()) {
197 Alert::query()
198 ->where([Alert::CREATED, '<', (time() - ($ttl * 3600))])
199 ->getQuery()
200 ->delete();
201 }
202 }
203
204 /**
205 * @throws IOException
206 * @throws \Exception
207 */
208 public function _systemCleanup() {
209 if($this->_queue_item_system->getType() != self::TYPE_DAILY) return;
210 $this->getLogController()->logMessage("\t [_systemCleanup]");
211 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
212 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
213
214 $this->getQueueItem()->updateStatus(Queue::STATUS_SYSTEM_SYSTEM_CLEANUP);
215 $this->getQueueItem()->updateProgress('System Cleanup');
216
217 $tmp_dir = Factory::getLocations()->getTempDir();
218 $dir = dir($tmp_dir);
219 $clearedFolders = 0;
220
221 while(($file = $dir->read()) !== false) {
222 $filepath = $tmp_dir . JetBackup::SEP . $file;
223
224 $this->getLogController()->logDebug("\t File: {$filepath}, mtime: " . filemtime($filepath) . ", TTL Threshold: " . (time() - self::TEMP_FILES_TTL));
225
226 if($file == '.' ||
227 $file == '..' ||
228 !is_dir($filepath) ||
229 (filemtime($filepath) > (time() - self::TEMP_FILES_TTL))
230 ) {
231 $this->getLogController()->logDebug("\t Skipping: {$filepath}");
232 continue;
233 }
234 $this->getLogController()->logDebug("\t Removing: {$filepath}");
235 Util::rm($filepath);
236 $clearedFolders++;
237 }
238
239 $dir->close();
240 if($clearedFolders > 0) Alert::add('System Cleanup', "Removed $clearedFolders temporary folders, refer to system logs for more details", Alert::LEVEL_INFORMATION);
241
242 $this->getLogController()->logMessage("Searching for old public restore files...");
243 // Check if there are public restore leftovers file is older than 24 hours
244 foreach (PreRestore::findPublicRestoreFiles() as $file) {
245 if (filemtime($file) < (time() - self::TEMP_FILES_TTL)) { //24 hours
246 $this->getLogController()->logMessage("Removing $file");
247 @unlink($file);
248 }
249 }
250
251
252
253 $this->getLogController()->logMessage("Clearing generated support users if exists");
254 Helper::clearSupportUser();
255
256 }
257
258 /**
259 * @return void
260 */
261 public function _processDailyAlerts() : void {
262
263 if($this->_queue_item_system->getType() != self::TYPE_DAILY) return;
264 $this->getLogController()->logMessage("\t [_processDailyAlerts]");
265 $this->getLogController()->logMessage("\t Execution time: {$this->getExecutionTimeElapsed()}");
266 $this->getLogController()->logMessage("\t TTL time: {$this->getExecutionTimeLimit()}");
267
268 $this->getQueueItem()->updateStatus(Queue::STATUS_SYSTEM_DAILY_ALERTS);
269 $this->getQueueItem()->updateProgress(Queue::STATUS_SYSTEM_NAMES[Queue::STATUS_SYSTEM_DAILY_ALERTS]);
270
271 try {
272 Alert::processDailyAlerts();
273 } catch (Exception $e) {
274 $this->getLogController()->logMessage('[_processDailyAlerts] Error : ' . $e->getMessage());
275 // just logging without breaking
276 }
277
278 }
279
280 /**
281 * @return void
282 * @throws HttpRequestException
283 * @throws NotificationException
284 */
285 public function _validateChecksums() {
286
287 if(
288 $this->_queue_item_system->getType() != self::TYPE_DAILY ||
289 !Factory::getSettingsSecurity()->isValidateChecksumsEnabled()
290 ) return;
291
292 $this->getLogController()->logMessage("\t [_validateChecksums]");
293 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
294 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
295
296 $this->getQueueItem()->updateStatus(Queue::STATUS_SYSTEM_VALIDATE_CHECKSUMS);
297 $this->getQueueItem()->updateProgress('Validate System Checksums');
298
299 $response = JetHttp::request()
300 ->setReturnTransfer()
301 ->setTimeout(5)
302 ->setFollowLocation()
303 ->exec(sprintf(self::CHECKSUM_URL, Wordpress::getVersion(), Wordpress::getLocale()));
304
305 if($response->getHeaders()->getCode() != 200 || !($body = $response->getBody())) return;
306
307 $files = [];
308 $homedir = Factory::getWPHelper()->getWordPressHomedir();
309 $body = json_decode($body);
310
311 foreach ($body->checksums as $file => $checksum) {
312
313 foreach (self::VALIDATE_CHECKSUMS_EXCLUDES as $pattern) {
314 if (fnmatch($pattern, $file)) continue 2;
315 }
316
317 $local_file = $homedir . $file;
318 if(!file_exists($local_file)) continue;
319
320 $local_checksum = md5_file($local_file);
321
322 if ($local_checksum !== $checksum) {
323 $files[] = [
324 'file' => $local_file,
325 'api_checksum' => $checksum,
326 'local_checksum' => $local_checksum,
327 ];
328 }
329 }
330
331
332 if(!sizeof($files)) return;
333 $this->getLogController()->logDebug("\t [_validateChecksums] Found files: " . print_r($files, true));
334 Notification::message()
335 ->addParam('backup_domain', Wordpress::getSiteDomain())
336 ->addParam('checksums', $files)
337 ->send('Checksum Verification Failed', 'checksum_alert');
338 }
339
340 /**
341 * @return void
342 * @throws InvalidArgumentException
343 * @throws \SleekDB\Exceptions\IOException
344 */
345 public function _retentionCleanup() {
346
347 try {
348 $this->getLogController()->logMessage("\t [_retentionCleanup]");
349 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
350 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
351
352 $this->getQueueItem()->updateProgress('Adding retention cleanup to queue');
353
354 $queue_item = QueueItem::prepare();
355 $queue_item->setType(Queue::QUEUE_TYPE_RETENTION_CLEANUP);
356 Queue::addToQueue($queue_item);
357 } catch (QueueException $e) {
358 $this->getLogController()->logMessage('[System] Adding retention cleanup failed: ' . $e->getMessage());
359 // just logging without breaking
360 }
361
362
363 }
364
365 /**
366 * @return void
367 * @throws DBException
368 */
369 public function _downloadsCleanup() {
370 try {
371 $this->getLogController()->logMessage("\t [_downloadsCleanup]");
372 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
373 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
374 $this->getQueueItem()->updateProgress('Cleaning download folder');
375
376 if (($ttl = Factory::getSettingsMaintenance()->getDownloadItemsTTL() * 3600) === 0) return;
377 Download::deleteByTTL($ttl);
378
379 } catch ( \SleekDB\Exceptions\IOException | InvalidArgumentException $e) {
380 $this->getLogController()->logMessage('[System] Cleaning download failed: ' . $e->getMessage());
381 // just logging without breaking
382 }
383 }
384
385 public function _logsCleanup() {
386 if($this->_queue_item_system->getType() != self::TYPE_DAILY) return;
387 $this->getLogController()->logMessage("\t [_logsCleanup]");
388 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
389 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
390
391 $this->getQueueItem()->updateStatus(Queue::STATUS_SYSTEM_LOGS_CLEANUP);
392 $this->getQueueItem()->updateProgress('Logs Cleanup');
393
394 $log_dir = Factory::getLocations()->getLogsDir();
395 $dir = dir($log_dir);
396
397 $all_logs = [];
398
399 while(($file = $dir->read()) !== false) {
400 if($file == '.' || $file == '..' || !preg_match("/_([^_]+)\.log$/", $file, $matches)) continue;
401 $filepath = $log_dir . JetBackup::SEP . $file;
402 $all_logs[$matches[1]][filemtime($filepath)] = $filepath;
403 }
404
405 $dir->close();
406
407 $keep_logs = Factory::getSettingsLogging()->getLogRotate();
408
409 foreach ($all_logs as $logs) {
410 if(sizeof($logs) <= $keep_logs) continue;
411 krsort($logs);
412
413 $skip = $keep_logs;
414
415 foreach ($logs as $log) {
416 if($skip) {
417 $skip--;
418 continue;
419 }
420
421 unlink($log);
422 }
423 }
424 }
425
426 /**
427 * @return void
428 * @throws InvalidArgumentException
429 * @throws NotificationException
430 * @throws DBException
431 * @throws \SleekDB\Exceptions\IOException
432 */
433 public function _backupJobMonitor() {
434 if($this->_queue_item_system->getType() != self::TYPE_DAILY) return;
435 $this->getLogController()->logMessage("\t [_backupJobMonitor]");
436 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
437 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
438
439 $this->getQueueItem()->updateStatus(Queue::STATUS_SYSTEM_JOB_MONITOR);
440 $this->getQueueItem()->updateProgress('Backup Jobs Monitor');
441
442
443 $backups = BackupJob::query()
444 ->getQuery()
445 ->fetch();
446
447 foreach($backups as $backup_details) {
448 $backup = new BackupJob( $backup_details[ JetBackup::ID_FIELD]);
449
450 if(
451 // skip if job never executed
452 !$backup->getLastRun() ||
453 // skip if monitor isn't set for this job
454 !$backup->getMonitor() ||
455 // skip if job was executed properly
456 ($backup->getLastRun() >= (time() - ($backup->getMonitor() * 86400)))
457 ) continue;
458
459 // send notification
460 Notification::message()
461 ->addParam('backup_domain', Wordpress::getSiteDomain())
462 ->addParam('job_name', $backup->getName())
463 ->addParam('job_monitor', $backup->getMonitor())
464 ->addParam('backup_date', Util::date('Y-m-d H:i:s', $backup->getLastRun()))
465 ->send('JetBackup Status Update', 'job_monitor');
466
467 }
468 }
469
470 /**
471 * @return void
472 * @throws IOException
473 */
474 public function _checkLicense() {
475 $this->getLogController()->logMessage("\t [_checkLicense]");
476 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
477 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
478 $this->getQueueItem()->updateProgress('Checking license');
479 $settings = Factory::getConfig();
480 if(time() < $settings->getLicenseNextCheck()) return;
481 $settings->setLicenseNextCheck(time() + License::LOCALKEY_FAIL_INTERVAL);
482 $settings->save();
483
484 try {
485 License::retrieveLocalKey();
486 $this->getLogController()->logMessage("\t [_checkLicense] License Key Valid");
487 } catch(LicenseException $e) {
488 $error = "Failed retrieving license Local Key. Error: " . $e->getMessage();
489 $this->getLogController()->logError($error);
490 Alert::add("License check failed", $error, Alert::LEVEL_CRITICAL);
491 }
492 }
493
494 /**
495 * @return void
496 * @throws InvalidArgumentException
497 * @throws \SleekDB\Exceptions\IOException
498 * @throws DBException
499 */
500 public function _indexJBBackups() {
501
502 $settings = Factory::getSettingsGeneral();
503
504 if(!$settings->isJBIntegrationEnabled() || !JetBackupLinux::isInstalled()) {
505 JetBackupLinux::deleteSnapshots();
506 return;
507 }
508
509 try {
510 $this->getLogController()->logMessage("\t [_indexJBBackups]");
511 $this->getLogController()->logMessage('Execution time: ' . $this->getExecutionTimeElapsed());
512 $this->getLogController()->logMessage('TTL time: ' . $this->getExecutionTimeLimit());
513 $this->getQueueItem()->updateProgress('Indexing JB Linux');
514 JetBackupLinux::checkRequirements();
515 } catch (JetBackupLinuxException $e) {
516 $settings->setJBIntegrationEnabled(false);
517 $settings->save();
518 Alert::add("JetBackup Linux integration has been disabled", "JetBackup linux integration has been disabled due to the following error: " . $e->getMessage() . ". After fixing this issue you will need to manually re-enabled it from the general settings page.", Alert::LEVEL_WARNING);
519 JetBackupLinux::deleteSnapshots();
520 return;
521 }
522
523 try {
524 JetBackupLinux::addToQueue();
525 } catch (Exception $e) {
526 return;
527 }
528 }
529 }