config->getUserValue( $this->userSession->getUser()?->getUID(), Application::APP_ID, UserPreference::TYPING_PRIVACY, Participant::PRIVACY_PRIVATE, ); return sha1(json_encode([ 'remoteHash' => $hash, 'manipulated' => [ 'config' => [ 'chat' => [ 'read-privacy', 'typing-privacy' => $typingIndicator, ], 'call' => [ 'blur-virtual-background', ], 'conversations' => [ 'list-style', ], ], ] ])); } /** * @return Http::STATUS_BAD_REQUEST */ public function logUnexpectedStatusCode(string $method, int $statusCode, string $logDetails = ''): int { if ($this->config->getSystemValueBool('debug')) { $this->logger->error('Unexpected status code ' . $statusCode . ' returned for ' . $method . ($logDetails !== '' ? "\n" . $logDetails : '')); } else { $this->logger->debug('Unexpected status code ' . $statusCode . ' returned for ' . $method . ($logDetails !== '' ? "\n" . $logDetails : '')); } return Http::STATUS_BAD_REQUEST; } protected function generateDefaultRequestOptions( ?string $cloudId, #[SensitiveParameter] ?string $accessToken, ): array { $options = [ 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates'), 'f7cloud' => [ 'allow_local_address' => $this->config->getSystemValueBool('allow_local_remote_servers'), ], 'headers' => [ 'Accept' => 'application/json', 'X-F7cloud-Federation' => 'true', 'OCS-APIRequest' => 'true', 'Accept-Language' => $this->l10nFactory->getUserLanguage($this->userSession->getUser()), ], 'timeout' => 5, ]; if ($cloudId !== null && $accessToken !== null) { $options['auth'] = [urlencode($cloudId), $accessToken]; } return $options; } protected function prependProtocolIfNotAvailable(string $url): string { if (!str_starts_with($url, 'http://') && !str_starts_with($url, 'https://')) { $url = 'https://' . $url; } return $url; } /** * @param 'get'|'post'|'put'|'delete' $verb * @throws CannotReachRemoteException */ protected function request( string $verb, ?string $cloudId, #[SensitiveParameter] ?string $accessToken, string $url, array $parameters, ): IResponse { $requestOptions = $this->generateDefaultRequestOptions($cloudId, $accessToken); if (!empty($parameters)) { $requestOptions['json'] = $parameters; } try { return $this->clientService->newClient()->{$verb}( $this->prependProtocolIfNotAvailable($url), $requestOptions ); } catch (ClientException $e) { $status = $e->getResponse()->getStatusCode(); try { $body = $e->getResponse()->getBody()->getContents(); $data = json_decode($body, true, flags: JSON_THROW_ON_ERROR); $e->getResponse()->getBody()->rewind(); if (!is_array($data)) { throw new \RuntimeException('JSON response is not an array'); } } catch (\Throwable $e) { throw new CannotReachRemoteException('Error parsing JSON response', $e->getCode(), $e); } $clientException = new RemoteClientException($e->getMessage(), $status, $e, $data); $this->logger->debug('Client error from remote', ['exception' => $clientException]); return new Response($e->getResponse(), false); } catch (ServerException|\Throwable $e) { $serverException = new CannotReachRemoteException($e->getMessage(), $e->getCode(), $e); $this->logger->error('Could not reach remote', ['exception' => $serverException]); throw $serverException; } } /** * @throws CannotReachRemoteException */ public function get( ?string $cloudId, #[SensitiveParameter] ?string $accessToken, string $url, array $parameters = [], ): IResponse { return $this->request( 'get', $cloudId, $accessToken, $url, $parameters, ); } /** * @throws CannotReachRemoteException */ public function put( string $cloudId, #[SensitiveParameter] string $accessToken, string $url, array $parameters = [], ): IResponse { return $this->request( 'put', $cloudId, $accessToken, $url, $parameters, ); } /** * @throws CannotReachRemoteException */ public function delete( string $cloudId, #[SensitiveParameter] string $accessToken, string $url, array $parameters = [], ): IResponse { return $this->request( 'delete', $cloudId, $accessToken, $url, $parameters, ); } /** * @throws CannotReachRemoteException */ public function post( string $cloudId, #[SensitiveParameter] string $accessToken, string $url, array $parameters = [], ): IResponse { return $this->request( 'post', $cloudId, $accessToken, $url, $parameters, ); } /** * @param list $allowedStatusCodes * @throws CannotReachRemoteException */ public function getOCSData(IResponse $response, array $allowedStatusCodes = [Http::STATUS_OK]): array { if (!in_array($response->getStatusCode(), $allowedStatusCodes, true)) { $this->logUnexpectedStatusCode(__METHOD__, $response->getStatusCode()); } try { $content = $response->getBody(); $responseData = json_decode($content, true, flags: JSON_THROW_ON_ERROR); if (!is_array($responseData)) { throw new \RuntimeException('JSON response is not an array'); } } catch (\Throwable $e) { $this->logger->error('Error parsing JSON response: ' . ($content ?? 'no-data'), ['exception' => $e]); throw new CannotReachRemoteException('Error parsing JSON response', $e->getCode(), $e); } return $responseData['ocs']['data'] ?? []; } }