PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / trunk
JetBackup – Backup, Restore & Migrate vtrunk
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 / Helper.php
backup / src / JetBackup / Wordpress Last commit date
.htaccess 1 year ago Abilities.php 1 day ago Blog.php 1 year ago Helper.php 5 months ago Init.php 5 months ago Installer.php 7 months ago MySQL.php 1 year ago UI.php 4 months ago Update.php 1 year ago Wordpress.php 1 day ago index.html 1 year ago web.config 1 year ago
Helper.php
357 lines
1 <?php
2
3 namespace JetBackup\Wordpress;
4
5 use Exception;
6 use JetBackup\Alert\Alert;
7 use JetBackup\Encryption\Crypt;
8 use JetBackup\Entities\Util;
9 use JetBackup\Exception\IOException;
10 use JetBackup\Factory;
11 use JetBackup\JetBackup;
12 use WP_User;
13
14 if (!defined( '__JETBACKUP__')) die('Direct access is not allowed');
15
16 class Helper {
17
18 private string $_homedir='';
19
20 private const SUPPORT_USER_TTL = 30 * 24 * 60 * 60; // 30 days in seconds
21
22 const MYSQL_AUTH_PATTERNS = [
23 'db_name' => "/define\s*\(\s*['\"]DB_NAME['\"]\s*,\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*\)\s*;/",
24 'db_user' => "/define\s*\(\s*['\"]DB_USER['\"]\s*,\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*\)\s*;/",
25 'db_password' => "/define\s*\(\s*['\"]DB_PASSWORD['\"]\s*,\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*\)\s*;/",
26 'db_host' => "/define\s*\(\s*['\"]DB_HOST['\"]\s*,\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*\)\s*;/",
27 'table_prefix' => "/^\s*\\\$table_prefix\s*=\s*['\"]((?:\\\\.|[^'\"])+)['\"]\s*;/",
28 ];
29
30 public static function isMainSite(): bool {
31 return (function_exists('is_main_site') && is_main_site());
32 }
33
34 public static function getMainSiteId(): int {
35 $main_site_id = 1;
36 if (function_exists('get_main_site_id')) return get_main_site_id();
37 if (function_exists('get_site_option')) $main_site_id = (int) get_site_option('main_site_id');
38 if ($main_site_id > 0) return $main_site_id;
39 return $main_site_id;
40 }
41
42 public static function isMultisite(): bool {
43 if (function_exists('is_multisite') && is_multisite()) return true;
44 if (defined( 'MULTISITE')) return MULTISITE;
45 if (defined( 'SUBDOMAIN_INSTALL') || defined( 'VHOST') || defined( 'SUNRISE')) return true;
46 return false;
47 }
48
49 public static function isNetworkAdminUser(): bool {
50 return function_exists('current_user_can') && current_user_can('manage_network') &&
51 function_exists('is_super_admin') && is_super_admin();
52 }
53
54 /**
55 * @throws Exception
56 */
57 public static function getWordpressUser($username) : ?array {
58
59 if(!function_exists('get_user_by')) throw new Exception("Function get_user_by() not defined");
60 if(!function_exists('wp_set_password')) throw new Exception("Function wp_set_password() not defined");
61
62 $user = get_user_by('login', $username);
63 if(!$user) return null;
64
65
66 $password = Util::generatePassword();
67 wp_set_password($password, $user->ID);
68
69 return [
70 'username' => $user->user_login,
71 'email' => $user->user_email,
72 'password' => $password,
73 'wordpress_admin_url' => Wordpress::getAdminURL(),
74
75 ];
76
77 }
78
79 /**
80 * @throws Exception
81 */
82 public static function clearSupportUser() : void {
83
84 $config = Factory::getConfig();
85 if(!$username = $config->getSupportUsername()) return;
86 if(!$user = get_user_by('login', $username)) return;
87 $user_created_date = $user->data->user_registered ?? null;
88 if(!$user_created_date) return;
89 if(!$user_created_timestamp = strtotime($user_created_date)) return;
90
91 if ((time() - $user_created_timestamp) > self::SUPPORT_USER_TTL) {
92
93 if (self::isMultisite()) {
94 if (!function_exists('wpmu_delete_user')) require_once WP_ROOT . JetBackup::SEP . 'wp-admin' . JetBackup::SEP . 'includes' . JetBackup::SEP . 'ms.php';
95 revoke_super_admin($user->ID); // super admin user is protected by wp_delete user, so we need to revoke first
96 wpmu_delete_user($user->ID);
97 } else {
98 if (!function_exists('wp_delete_user')) require_once WP_ROOT . JetBackup::SEP . 'wp-admin' . JetBackup::SEP . 'includes' . JetBackup::SEP . 'user.php';
99 wp_delete_user($user->ID);
100 }
101 $config->setSupportUsername('');
102 $config->save();
103 Alert::add('System Cleanup', "Temporary support user '{$user->data->user_login}' created 30 days ago removed", Alert::LEVEL_INFORMATION);
104 }
105
106 }
107
108 /**
109 * @throws IOException
110 * @throws Exception
111 */
112 public static function createSupportUser() : array {
113
114 $config = Factory::getConfig();
115 $username = $config->getSupportUsername();
116 if ($username && $output = self::getWordpressUser($username)) return $output;
117
118 if(!function_exists('wp_create_user')) throw new Exception("Function wp_create_user() not defined");
119 if(!function_exists('grant_super_admin')) throw new Exception("Function grant_super_admin() not defined");
120
121 // Generate new user details
122 $email = "support+" . Util::generateRandomString(5) . "@jetbackup.com";
123 $password = Util::generatePassword();
124 $username = "jetbackup_" . Util::generateRandomString(5);
125
126 // Create new user
127 $user_id = wp_create_user($username, $password, $email);
128 if (is_wp_error($user_id)) throw new Exception($user_id->get_error_message());
129
130 $user = new WP_User($user_id);
131 $user->set_role('administrator');
132 if (self::isMultisite()) grant_super_admin($user_id); // Grant network admin privileges
133
134 $config->setSupportUsername($username);
135 $config->save();
136
137 return [
138 'username' => $user->user_login,
139 'email' => $email,
140 'password' => $password,
141 'wordpress_admin_url' => Wordpress::getAdminURL(),
142 ];
143
144 }
145
146 public static function isAdminUser(): bool {
147 return function_exists('current_user_can') && current_user_can('manage_options') &&
148 function_exists('is_super_admin') && is_super_admin();
149 }
150
151 /**
152 * Returns true/false if I am in the network admin GUI interface (not if I am network admin USER)
153 * @return bool
154 */
155 public static function isNetworkAdminInterface(): bool {
156 if (function_exists('is_network_admin') && is_network_admin()) return true;
157 if (isset($GLOBALS['current_screen'])) return $GLOBALS['current_screen']->in_admin( 'network' );
158 elseif (defined('WP_NETWORK_ADMIN')) return WP_NETWORK_ADMIN;
159 return false;
160 }
161
162 /**
163 * @param bool $public
164 * Example output: /home/user/public_html/
165 * public flag: public_html (no ending /)
166 * @return string
167 */
168 public function getWordPressHomedir(bool $public = false): string {
169
170 if (!$this->_homedir) {
171 $homedir = defined('WP_ROOT') ? WP_ROOT : Wordpress::getAbsPath();
172 $this->_homedir = rtrim($homedir, JetBackup::SEP) . JetBackup::SEP;
173 }
174
175 if ($public) return basename(rtrim($this->_homedir, JetBackup::SEP));
176 return $this->_homedir;
177 }
178
179 /**
180 * @return string
181 * Returns public restore file location
182 * Example -
183 * Alternate path enabled: /home/user/public_html/wp-content/plugins/backup/public/cron
184 * Alternate path disabled: /home/user/public_html
185 */
186 public function getRestoreFileLocation() : string {
187 if (Factory::getSettingsRestore()->isRestoreAlternatePathEnabled()) return rtrim(JetBackup::CRON_PATH, JetBackup::SEP);
188 return rtrim($this->getWordPressHomedir(), JetBackup::SEP);
189 }
190
191 /**
192 * @return string
193 * Return's WordPress public_dir relative to homedir, needed for nested sites (sites inside subfolders)
194 * Example
195 * - getWordPressHomedir: /home/user/sites/www.mydomain.com/subfolder
196 * - getUserHomedir: /home/user
197 * - getWordPressRelativePublicDir: /sites/wp2.jetbackup.com/subfolder
198 */
199 public function getWordPressRelativePublicDir() : string {
200
201 $public_dir = trim($this->getWordPressHomedir(), JetBackup::SEP);
202 $getUserHomedir = trim($this->getUserHomedir(), JetBackup::SEP);
203 $relative_path = $getUserHomedir;
204 if ($public_dir != $getUserHomedir && str_starts_with($public_dir, $getUserHomedir)) {
205 $relative_path = trim(substr($public_dir, strlen($getUserHomedir)), JetBackup::SEP);
206 }
207
208 return JetBackup::SEP . $relative_path;
209 }
210
211 /**
212 * @return string
213 * Example output: /home/user
214 */
215
216 public static function getUserHomedir(): ?string {
217
218 $user_details = Util::getpwuid(Util::geteuid());
219 return $user_details['dir'] ?? null;
220
221 }
222
223 /**
224 * @param string|null $decryption_key Optional key to decrypt runtime credentials (queue unique ID)
225 * @throws Exception
226 */
227 public static function parseWpConfig(?string $decryption_key = null): \stdClass {
228
229 // Check if runtime credentials are available (from restore file)
230 // This handles cloud environments (WordPress.com, WP Cloud, Porkbun) where
231 // wp-config.php doesn't contain literal credentials
232 if (defined('JB_RUNTIME_CREDENTIALS') && $decryption_key) {
233 $decrypted = Crypt::decrypt(JB_RUNTIME_CREDENTIALS, $decryption_key);
234 $creds = json_decode($decrypted);
235
236 if ($creds && !empty($creds->db_name)) {
237 $output = new \stdClass();
238 $output->db_name = $creds->db_name;
239 $output->db_user = $creds->db_user;
240 $output->db_password = $creds->db_password;
241 $output->db_host = $creds->db_host;
242 $output->table_prefix = $creds->table_prefix ?? 'wp_';
243 $output->db_port = Factory::getSettingsGeneral()->getMySQLDefaultPort();
244
245 // Handle host:port notation
246 if (strpos($output->db_host, ':') !== false) {
247 list($output->db_host, $output->db_port) = explode(':', $output->db_host, 2);
248 }
249
250 return $output;
251 }
252 }
253
254 // Fall back to parsing wp-config.php file
255 $config_file = Factory::getSettingsGeneral()->getAlternateWpConfigLocation();
256 if(!file_exists($config_file)) throw new Exception("The wp-config.php file does not exist at the specified path. ($config_file)");
257
258 $output = new \stdClass();
259
260 if(!($f = fopen($config_file, 'r'))) throw new Exception("Unable to open the wp-config.php file.");
261
262 $patterns = self::MYSQL_AUTH_PATTERNS;
263
264 while (($line = fgets($f)) !== false) {
265 $line = trim($line);
266 // Skip commented lines
267 if (strpos($line, '//') === 0 || strpos($line, '#') === 0) continue;
268
269 foreach ($patterns as $key => $pattern) {
270 if (!preg_match($pattern, $line, $matches)) continue;
271 $value = stripcslashes($matches[1]);
272
273 if ($key === 'db_host' && strpos($value, ':') !== false) {
274 list($output->db_host, $output->db_port) = explode(':', $value, 2);
275 } else {
276 $output->{$key} = $value;
277 }
278
279 unset($patterns[$key]);
280 break; // Break the foreach loop if a pattern matches
281 }
282
283 // Break the while loop if all patterns are found
284 if (empty($patterns)) break;
285 }
286
287 fclose($f);
288
289 // Set default port if not found
290 if (!isset($output->db_port)) $output->db_port = Factory::getSettingsGeneral()->getMySQLDefaultPort(); // Default MySQL port
291 if (!isset($output->db_name)) $output->db_name = defined('DB_NAME') ? DB_NAME : '';
292 if (!isset($output->db_user)) $output->db_user = defined('DB_USER') ? DB_USER : '';
293 if (!isset($output->db_password)) $output->db_password = defined('DB_PASSWORD') ? DB_PASSWORD : '';
294 if (!isset($output->db_host)) $output->db_host = defined('DB_HOST') ? DB_HOST : '';
295
296 return $output;
297
298 }
299
300 public function getUploadDir(): string {
301 $_uploads = Wordpress::getUploadDir()['basedir'] ?? 'uploads';
302 return Wordpress::WP_CONTENT . JetBackup::SEP . basename($_uploads);
303 }
304
305 public static function validateEmail($email): bool {
306 // Ensure $email is an array
307 $emails = is_array($email) ? $email : [$email];
308
309 foreach ($emails as $singleEmail) {
310 $sanitizedEmail = Wordpress::sanitizeEmail($singleEmail);
311 if (!Wordpress::isEmail($sanitizedEmail)) return false; // Invalid email found
312 }
313
314 return true;
315 }
316
317 static function getCurrentScreen():?string {
318 if($screen = Wordpress::getCurrentScreen()) return $screen->id;
319 return null;
320 }
321
322 public static function getUserId():?int {
323 if($user = Wordpress::getCurrentUser()) return $user->ID;
324 return null;
325 }
326
327 public static function getUserEmail():?string {
328 if($user = Wordpress::getCurrentUser()) return $user->user_email;
329 return null;
330 }
331
332 public static function getUserIP(): ?string {
333 $ip = $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? null;
334 if ($ip === null) return null;
335 //If forwarded IPs are present, take the first one
336 if ( Wordpress::strContains( $ip, ',' ) ) {$ip = trim(explode(',', $ip)[0]);}
337 $ip = Wordpress::sanitizeTextField($ip);
338 if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip;
339 return null;
340 }
341
342
343
344 public static function isWPCli(): bool {
345 return (defined('WP_CLI') && WP_CLI);
346 }
347
348
349 public static function isCLI():bool {
350
351 if (isset($_SERVER['HTTP_TE']) || isset($_SERVER['HTTP_COOKIE']) || isset($_SERVER['HTTP_ACCEPT'])) return false;
352 if(self::isWPCli() || in_array( PHP_SAPI, ['cli', 'cli-server']) || defined('STDIN')) return true;
353 return isset($_SERVER['argv']) && sizeof($_SERVER['argv']);
354
355 }
356 }
357