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