PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / trunk
JetBackup – Backup, Restore & Migrate vtrunk
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 / Wordpress / Init.php
backup / src / JetBackup / Wordpress Last commit date
.htaccess 1 year ago Abilities.php 1 month ago Blog.php 1 year ago Helper.php 6 months ago Init.php 4 days ago Installer.php 9 months ago MySQL.php 1 year ago UI.php 6 months ago Update.php 1 year ago Wordpress.php 1 month ago index.html 1 year ago web.config 1 year ago
Init.php
294 lines
1 <?php
2
3 namespace JetBackup\Wordpress;
4
5 use JetBackup\BackupJob\BackupJob;
6 use JetBackup\CLI\CLI;
7 use JetBackup\Destination\Destination;
8 use JetBackup\Download\Download;
9 use JetBackup\Downloader\Downloader;
10 use JetBackup\Entities\Util;
11 use JetBackup\Exception\JBException;
12 use JetBackup\Factory;
13 use JetBackup\JetBackup;
14 use JetBackup\Queue\QueueItem;
15 use JetBackup\Schedule\Schedule;
16 use JetBackup\SGB\Migration;
17 use JetBackup\UserInput\UserInput;
18
19 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
20
21 class Init {
22
23 private static bool $_initialized = false;
24
25 private function __construct() { } // only static method
26
27 public static function isInitialized(): bool {
28 return self::$_initialized;
29 }
30
31 /**
32 * Detect whether the site is likely running on WP Cloud / Atomic (e.g., Porkbun WP Cloud).
33 *
34 * Logic:
35 * 1) IS_ATOMIC must exist and evaluate to true
36 * 2) AND at least one of ATOMIC_CLIENT_ID / ATOMIC_SITE_ID must exist (to reduce false positives)
37 */
38 public static function isWpCloudAtomic(): bool
39 {
40 // Fast fail: constant not present at all
41 if (!\defined('IS_ATOMIC')) {
42 return false;
43 }
44
45 // Some platforms may define it as bool/int/string; normalize safely
46 $isAtomic = \constant('IS_ATOMIC');
47
48 // Treat 1 / "1" / true / "true" as true
49 $isAtomic = ($isAtomic === true)
50 || ($isAtomic === 1)
51 || ($isAtomic === '1')
52 || (\is_string($isAtomic) && \strcasecmp($isAtomic, 'true') === 0);
53
54 if (!$isAtomic) {
55 return false;
56 }
57
58 // Extra signals to avoid collisions with other platforms that might define IS_ATOMIC
59 $hasClientId = \defined('ATOMIC_CLIENT_ID') && \constant('ATOMIC_CLIENT_ID') !== null && \constant('ATOMIC_CLIENT_ID') !== '';
60 $hasSiteId = \defined('ATOMIC_SITE_ID') && \constant('ATOMIC_SITE_ID') !== null && \constant('ATOMIC_SITE_ID') !== '';
61
62 return ($hasClientId || $hasSiteId);
63 }
64
65 /**
66 * Run a callable only if JetBackup init completed successfully.
67 * Never throws (logs and returns default).
68 *
69 * @param callable $callable
70 * @param array $args
71 * @param mixed $default
72 * @return mixed
73 */
74 public static function guard(callable $callable, array $args = [], $default = null) {
75 try {
76 if (!self::isInitialized()) return $default;
77 return \call_user_func_array($callable, $args);
78 } catch (\Throwable $e) {
79 error_log('[JetBackup] guarded hook failed: ' . $e->getMessage());
80 return $default;
81 }
82 }
83
84 /**
85 * @return void
86 */
87 public static function actionInit() {
88 if (!function_exists('current_user_can') || !current_user_can('manage_options')) return;
89
90 try {
91
92 load_plugin_textdomain('jetbackup', false, JetBackup::PLUGIN_NAME . DIRECTORY_SEPARATOR . 'languages');
93
94 // Cookie Env (safe)
95 add_action('wp_loaded', ['\JetBackup\Wordpress\Wordpress', 'setNonceCookie']);
96 add_action('wp_loaded', ['\JetBackup\Wordpress\Wordpress', 'setUserLanguageCookie']);
97
98 self::_createWorkingSpace();
99 self::_validateWorkingSpace();
100
101 (new Migration())->migrate();
102 Destination::createDefaultDestination();
103 Schedule::createDefaultSchedule();
104
105 BackupJob::getDefaultJob();
106 BackupJob::getDefaultConfigJob();
107
108 // On multisite, only a Super Admin on the main site may proceed. This gate MUST run
109 // BEFORE _download() reads request input; otherwise a per-site administrator (who has
110 // manage_options) could stream the whole-network backup or job logs via the
111 // download_id / queue_item_id request parameters.
112 $hookNetworkMenu = false;
113 if (Helper::isMultisite()) {
114
115 if (!Helper::isMainSite() || !Helper::isNetworkAdminUser()) return;
116 $hookNetworkMenu = Helper::isNetworkAdminInterface();
117 }
118
119 self::_download();
120
121 //Only register UI/AJAX/heartbeat after we know init succeeded.
122 if ($hookNetworkMenu) add_action('network_admin_menu', ['\JetBackup\Wordpress\UI', 'main']);
123
124 self::$_initialized = true;
125
126 add_action('admin_menu', ['\JetBackup\Wordpress\UI', 'main']);
127 add_action('wp_ajax_jetbackup_api', ['\JetBackup\Ajax\Ajax', 'main']);
128
129 if (Factory::getSettingsAutomation()->isHeartbeatEnabled()) {
130 add_action('admin_footer', ['\JetBackup\Wordpress\UI', 'heartbeat']);
131 add_action('wp_ajax_jetbackup_heartbeat', ['\JetBackup\Ajax\Ajax', 'heartbeat']);
132 }
133
134 if (self::isWpCloudAtomic()) {
135
136 $restore = Factory::getSettingsRestore();
137 $changed = false;
138
139 if (!$restore->isRestoreWpContentOnlyEnabled()) {
140 $restore->setRestoreWpContentOnly(true);
141 $changed = true;
142 }
143
144 if (!$restore->isRestoreAlternatePathEnabled()) {
145 $restore->setRestoreAlternatePath(true);
146 $changed = true;
147 }
148
149 if ($changed) $restore->save();
150
151 }
152
153
154 } catch (\Throwable $e) {
155
156 error_log('[JetBackup] actionInit failed: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
157
158 // if any hooks were added earlier, remove them
159 remove_action('admin_menu', ['\JetBackup\Wordpress\UI', 'main']);
160 remove_action('network_admin_menu', ['\JetBackup\Wordpress\UI', 'main']);
161 remove_action('admin_footer', ['\JetBackup\Wordpress\UI', 'heartbeat']);
162 remove_action('wp_ajax_jetbackup_api', ['\JetBackup\Ajax\Ajax', 'main']);
163 remove_action('wp_ajax_jetbackup_heartbeat', ['\JetBackup\Ajax\Ajax', 'heartbeat']);
164
165 if (function_exists('is_admin') && is_admin()) {
166 add_action('admin_notices', function () use ($e) {
167 if (!current_user_can('manage_options')) return;
168 echo '<div class="notice notice-error"><p><strong>JetBackup:</strong> '
169 . esc_html('JetBackup failed to initialize and was disabled for this request. Reason: ' . $e->getMessage())
170 . '</p></div>';
171 });
172 }
173
174 return;
175 }
176 }
177
178
179
180 private static function _download():void {
181 try {
182 $userInput = new UserInput();
183 $userInput->setData($_REQUEST);
184
185 if($download_id = $userInput->getValidated('download_id', 0, UserInput::UINT)) {
186 $download = new Download($download_id);
187 if(!$download->getId()) throw new JBException('The provided download id not found');
188 $download->download();
189 }
190
191 if($queue_item_id = $userInput->getValidated('queue_item_id', 0, UserInput::UINT)) {
192 $queue_item = new QueueItem($queue_item_id);
193 if(!$queue_item->getId()) throw new JBException('The provided queue item id not found');
194 $downloader = new Downloader($queue_item->getLogFile());
195 $downloader->download();
196 }
197 } catch(JBException $e) {
198 wp_die(esc_html('JetBackup: ' . $e->getMessage()));
199 }
200 }
201
202 private static function _getWorkingSpaceLockFile(): string
203 {
204 return Factory::getLocations()->getDataDir()
205 . JetBackup::SEP
206 . Factory::getConfig()->getUniqueID()
207 . '.lock';
208 }
209
210 private static function _getWorkingSpaceLockFileValue(): string
211 {
212 $lockFile = self::_getWorkingSpaceLockFile();
213 if (!file_exists($lockFile)) return '';
214 $content = @file_get_contents($lockFile);
215 if ($content === false) return ''; // Treat as corrupted; let validator handle it
216 return trim($content);
217 }
218
219 private static function _getInstallFingerprint(): string
220 {
221 global $wpdb;
222
223 $dbName = $wpdb->dbname ?? '';
224 $prefix = $wpdb->prefix ?? '';
225
226 $secret = defined('AUTH_KEY')
227 ? AUTH_KEY
228 : Factory::getConfig()->getEncryptionKey();
229
230 return sha1($dbName . '|' . $prefix . '|' . $secret);
231 }
232
233 private static function _updateWorkingSpaceLockFile(): void
234 {
235 $lockFile = self::_getWorkingSpaceLockFile();
236 $fingerprint = self::_getInstallFingerprint();
237
238 if (file_put_contents($lockFile, $fingerprint, LOCK_EX) !== false) {
239 @chmod($lockFile, 0400);
240 }
241 }
242
243 private static function _validateWorkingSpace(): void
244 {
245 // Only lock the folder if we are using an alternate datadir
246 // Regular setup is inside wp-content which is per-install
247 if (empty(Factory::getConfig()->getAlternateDataFolder())) return;
248
249 $lockFile = self::_getWorkingSpaceLockFile();
250
251 // First run: create lock file and exit
252 if (!file_exists($lockFile)) {
253 self::_updateWorkingSpaceLockFile();
254 return;
255 }
256
257 $storedFingerprint = self::_getWorkingSpaceLockFileValue();
258 $currentFingerprint = self::_getInstallFingerprint();
259
260 if (!hash_equals($storedFingerprint, $currentFingerprint)) {
261 error_log('JetBackup: Alternate data folder reset due to mismatched installation fingerprint.');
262 Factory::getConfig()->setAlternateDataFolder('');
263 Factory::getConfig()->save();
264 }
265 }
266
267
268 private static function _createWorkingSpace() {
269
270 $folders = [
271 Factory::getLocations()->getDataDir(),
272 Factory::getLocations()->getTempDir(),
273 Factory::getLocations()->getDatabaseDir(),
274 Factory::getLocations()->getDownloadsDir(),
275 Factory::getLocations()->getBackupsDir(),
276 Factory::getLocations()->getLogsDir(),
277 ];
278
279 foreach ($folders as $folder) Util::secureFolder($folder);
280
281 }
282
283 public static function filterAdminBodyClass($classes) {
284 $screen = Helper::getCurrentScreen();
285 if ($screen && strpos($screen, 'jetbackup') !== false) $classes .= ' jetbackup';
286 return $classes;
287 }
288
289 public static function actionCLI() {
290 if (defined('WP_CLI') && WP_CLI) CLI::init();
291 }
292
293 }
294