httpClient = $httpClientService->newClient(); } /** * run the job, then remove it from the joblist */ public function start(IJobList $jobList): void { $target = $this->argument['url']; // only execute if target is still in the list of trusted domains if ($this->trustedServers->isTrustedServer($target)) { $this->parentStart($jobList); } $jobList->remove($this, $this->argument); if ($this->retainJob) { $this->reAddJob($this->argument); } } /** * Call start() method of parent * Useful for unit tests */ protected function parentStart(IJobList $jobList): void { parent::start($jobList); } /** * @param array $argument * @return void */ protected function run($argument) { $target = $argument['url']; $created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime(); $currentTime = $this->time->getTime(); $source = $this->urlGenerator->getAbsoluteURL('/'); $source = rtrim($source, '/'); $token = $argument['token']; // kill job after 30 days of trying $deadline = $currentTime - $this->maxLifespan; if ($created < $deadline) { $this->logger->warning("The job to request the shared secret job is too old and gets stopped now without retention. Setting server status of '{$target}' to failure."); $this->retainJob = false; $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE); return; } $endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING'); $endPoint = $endPoints['shared-secret'] ?? $this->defaultEndPoint; // make sure that we have a well formatted url $url = rtrim($target, '/') . '/' . trim($endPoint, '/'); try { $result = $this->httpClient->post( $url, [ 'body' => [ 'url' => $source, 'token' => $token, 'format' => 'json', ], 'timeout' => 3, 'connect_timeout' => 3, ] ); $status = $result->getStatusCode(); } catch (ClientException $e) { $status = $e->getCode(); if ($status === Http::STATUS_FORBIDDEN) { $this->logger->info($target . ' refused to ask for a shared secret.', ['app' => 'federation']); } else { $this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage(), ['app' => 'federation']); } } catch (RequestException $e) { $status = -1; // There is no status code if we could not connect $this->logger->info('Could not connect to ' . $target, ['app' => 'federation']); } catch (\Throwable $e) { $status = Http::STATUS_INTERNAL_SERVER_ERROR; $this->logger->error($e->getMessage(), ['app' => 'federation', 'exception' => $e]); } // if we received a unexpected response we try again later if ( $status !== Http::STATUS_OK && ($status !== Http::STATUS_FORBIDDEN || $this->getAttempt($argument) < 5) ) { $this->retainJob = true; } } /** * re-add background job */ protected function reAddJob(array $argument): void { $url = $argument['url']; $created = isset($argument['created']) ? (int)$argument['created'] : $this->time->getTime(); $token = $argument['token']; $attempt = $this->getAttempt($argument) + 1; $this->jobList->add( RequestSharedSecret::class, [ 'url' => $url, 'token' => $token, 'created' => $created, 'attempt' => $attempt ] ); } protected function getAttempt(array $argument): int { return $argument['attempt'] ?? 0; } }