connections = $connections; $this->client = $client; } /** @return array{connection: array, peer: array, tables: array, files: ?array} */ public function connect(string $connection_string, string $label, bool $allow_insecure, ?array $basic_auth = null): array { $before = array_column($this->connections->all(), 'id'); $row = $this->connections->save($connection_string, $label, $allow_insecure, $basic_auth); try { $described = $this->describe($row); } catch (RelayError $e) { if (!in_array($row['id'], $before, true)) { $this->connections->delete($row['id']); // a connection that never worked is not kept } throw $e; } return ['connection' => $this->connections->get($row['id'])] + $described; } /** * Handshake, tables and file inventory: what Pull/Push show about the other site. A network shows its subsites * first; with $subsite (one of them) the tables and files are that subsite's, and "subsite" says which it is. * * @return array{peer: array, tables: array, files: ?array, subsites?: array[], subsite?: array} */ public function describe(array $connection, int $subsite = 0): array { $hello = $this->client->call($connection, 'hello')['meta']; SiteInfo::refuse_this_site((array) ($hello['site'] ?? [])); $features = array_values(array_filter((array) ($hello['features'] ?? []), 'is_string')); $network = in_array('subsites', $features, true); if ($subsite > 0 && !$network) { throw self::no_subsites(esc_html((string) ($hello['site']['home_url'] ?? ''))); } $described = in_array('pull', (array) ($hello['permissions'] ?? []), true) || in_array('push', (array) ($hello['permissions'] ?? []), true) ? $this->client->call($connection, 'db.describe', '', $subsite > 0 ? ['subsite' => $subsite] : [])['meta'] : []; $out = [ 'peer' => [ 'site' => $hello['site'] ?? [], 'mode' => (string) ($hello['mode'] ?? ''), 'permissions' => array_values(array_intersect(['pull', 'push'], (array) ($hello['permissions'] ?? []))), 'key' => $hello['key'] ?? [], 'protocol' => (int) ($hello['protocol'] ?? 0), 'features' => $features, ], 'tables' => (array) ($described['tables'] ?? []), 'files' => is_array($described['files'] ?? null) ? $described['files'] : null, ]; if ($network) { $out['subsites'] = array_values(array_filter((array) ($described['subsites'] ?? []), 'is_array')); } if ($subsite > 0) { $site = SiteInfo::from_peer($described['site'] ?? null); $out['subsite'] = ['id' => $subsite, 'name' => $site['name'], 'home_url' => $site['home_url']]; } return $out; } /** The other site's Relay can't choose subsites: it is older, or not a network. */ public static function no_subsites(string $site): RelayError { return new RelayError( 'remote.no_subsites', /* translators: %s: other site's address */ esc_html(sprintf(__('%s can\'t send or receive one subsite: it isn\'t a network, or its SiteRelay is older.', 'siterelay'), $site)), esc_html__('Update SiteRelay on the other site, or don\'t choose a subsite there.', 'siterelay') ); } }