wp_generate_uuid4(), 'kind' => $kind, 'previous' => self::fingerprint(), 'identity' => Store::identity(), 'user' => get_current_user_id(), 'session' => hash('sha256', wp_get_session_token()), 'created' => time()]; if ($kind === 'connect') $job['secret'] = Store::encrypt(trim($token)); if (! Store::add('connection_change', $job)) throw new \RuntimeException('connection_busy'); wp_clear_scheduled_hook('flx_security_refresh'); self::schedule(); self::process(); } public static function schedule(): void { if (! wp_next_scheduled('flx_security_connection_change')) wp_schedule_single_event(time() + 2, 'flx_security_connection_change'); } public static function process(): void { if (! self::pending()) return; $owner = Store::lock(); if ($owner === false) { self::schedule(); return; } $start = false; try { $job = self::pending(); if (! $job) return; if ($job['created'] < time() - 900 || ! user_can($job['user'], is_multisite() ? 'manage_network_options' : 'manage_options') || ! hash_equals($job['previous'], self::fingerprint())) throw new \RuntimeException('connection_changed'); Store::remove('sync'); Store::prune(); wp_clear_scheduled_hook('flx_security_continue'); if ($job['kind'] === 'connect') { Client::connect(Store::decrypt($job['secret']), 10); if ((self::pending()['id'] ?? null) !== $job['id']) { Disconnect::request(); $connection = Store::get('connection'); if ($connection) { $connection['paused'] = true; Store::set('connection', $connection); } throw new \RuntimeException('connection_changed'); } $start = true; $key = 'connection_saved'; } else { $remote = Disconnect::request(); Store::remove('connection'); Store::remove('error'); Plugin::deactivate(); $key = $remote ? 'disconnected' : 'disconnect_remote_failed'; } Store::setPersonal('feedback', ['user' => $job['user'], 'session' => $job['session'], 'key' => $key, 'error' => $key === 'disconnect_remote_failed'], $job['identity']); } catch (\Throwable $error) { if (isset($job['identity'])) Store::setPersonal('feedback', ['user' => $job['user'], 'session' => $job['session'], 'key' => 'error_'.I18n::errorCode($error->getMessage()), 'error' => true], $job['identity']); } finally { try { if (isset($job['id']) && (self::pending()['id'] ?? null) === $job['id']) Store::remove('connection_change'); } finally { Store::unlock($owner); wp_clear_scheduled_hook('flx_security_connection_change'); } } if ($start) { try { Sync::start(true); } catch (\Throwable $error) { Store::set('error', I18n::errorCode($error->getMessage())); } } Plugin::ensure(); } }