cleanURL($subscription['source']); if ($url === null) { return null; } // ICS feeds hosted on O365 can return HTTP 500 when the UA string isn't satisfactory // Ref https://github.com/nextcloud/calendar/issues/7234 $uaString = 'Nextcloud Webcal Service'; if (parse_url($url, PHP_URL_HOST) === 'outlook.office365.com') { // The required format/values here are not documented. // Instead, this string based on research. // Ref https://github.com/bitfireAT/icsx5/discussions/654#discussioncomment-14158051 $uaString = 'Nextcloud (Linux) Chrome/66'; } $allowLocalAccess = $this->config->getValueString('dav', 'webcalAllowLocalAccess', 'no'); $params = [ 'nextcloud' => [ 'allow_local_address' => $allowLocalAccess === 'yes', ], RequestOptions::HEADERS => [ 'User-Agent' => $uaString, 'Accept' => 'text/calendar, application/calendar+json, application/calendar+xml', ], 'stream' => true, ]; $user = parse_url($subscription['source'], PHP_URL_USER); $pass = parse_url($subscription['source'], PHP_URL_PASS); if ($user !== null && $pass !== null) { $params[RequestOptions::AUTH] = [$user, $pass]; } try { $client = $this->clientService->newClient(); $response = $client->get($url, $params); } catch (LocalServerException $ex) { $this->logger->warning("Subscription $subscriptionId was not refreshed because it violates local access rules", [ 'exception' => $ex, ]); return null; } catch (Exception $ex) { $this->logger->warning("Subscription $subscriptionId could not be refreshed due to a network error", [ 'exception' => $ex, ]); return null; } $contentType = $response->getHeader('Content-Type'); $contentType = explode(';', $contentType, 2)[0]; $format = match ($contentType) { 'application/calendar+json' => 'jcal', 'application/calendar+xml' => 'xcal', default => 'ical', }; // With 'stream' => true, getBody() returns the underlying stream resource $stream = $response->getBody(); if (!is_resource($stream)) { return null; } return ['data' => $stream, 'format' => $format]; } /** * This method will strip authentication information and replace the * 'webcal' or 'webcals' protocol scheme * * @param string $url * @return string|null */ private function cleanURL(string $url): ?string { $parsed = parse_url($url); if ($parsed === false) { return null; } if (isset($parsed['scheme']) && $parsed['scheme'] === 'http') { $scheme = 'http'; } else { $scheme = 'https'; } $host = $parsed['host'] ?? ''; $port = isset($parsed['port']) ? ':' . $parsed['port'] : ''; $path = $parsed['path'] ?? ''; $query = isset($parsed['query']) ? '?' . $parsed['query'] : ''; $fragment = isset($parsed['fragment']) ? '#' . $parsed['fragment'] : ''; $cleanURL = "$scheme://$host$port$path$query$fragment"; // parse_url is giving some weird results if no url and no :// is given, // so let's test the url again $parsedClean = parse_url($cleanURL); if ($parsedClean === false || !isset($parsedClean['host'])) { return null; } return $cleanURL; } }