131 lines
3.7 KiB
PHP
131 lines
3.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Mummert\CalDavSyncBundle\CalDav\Remote;
|
|
|
|
use Mummert\CalDavSyncBundle\CalDav\Parser\CalDavXmlParser;
|
|
use Mummert\CalDavSyncBundle\CalDav\Parser\ICalendarParser;
|
|
use Mummert\CalDavSyncBundle\CalDav\Transport\CalDavTransportInterface;
|
|
use Mummert\CalDavSyncBundle\Config\CalendarSyncConfig;
|
|
use Mummert\CalDavSyncBundle\Sync\RemoteEvent;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
final readonly class RemoteCalendarReader
|
|
{
|
|
public function __construct(
|
|
private CalDavTransportInterface $transport,
|
|
private CalDavXmlParser $xmlParser,
|
|
private ICalendarParser $icalendarParser,
|
|
private LoggerInterface $logger,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @return list<RemoteEvent>
|
|
*/
|
|
public function readEvents(CalendarSyncConfig $config): array
|
|
{
|
|
$response = $this->transport->report(
|
|
$config->caldavUrl,
|
|
$config->caldavUsername,
|
|
$config->caldavPassword,
|
|
$this->buildCalendarQueryBody(),
|
|
1,
|
|
['Content-Type' => 'application/xml; charset=utf-8'],
|
|
);
|
|
|
|
if (!in_array($response->statusCode, [200, 207], true)) {
|
|
$this->logger->warning('CalDAV calendar query failed.', [
|
|
'calendarId' => $config->calendarId,
|
|
'url' => $config->caldavUrl,
|
|
'statusCode' => $response->statusCode,
|
|
]);
|
|
|
|
return [];
|
|
}
|
|
|
|
$parsedItems = $this->xmlParser->parseCalendarMultistatus($response->body);
|
|
$events = [];
|
|
|
|
foreach ($parsedItems as $item) {
|
|
$event = $this->icalendarParser->parseEvent(
|
|
$item['href'],
|
|
$item['etag'],
|
|
$item['calendarData'],
|
|
$config->timezoneOrDefault(),
|
|
);
|
|
|
|
if (null !== $event) {
|
|
$events[] = $event;
|
|
}
|
|
}
|
|
|
|
return $events;
|
|
}
|
|
|
|
public function readEventByHref(CalendarSyncConfig $config, string $href): ?RemoteEvent
|
|
{
|
|
$response = $this->transport->get(
|
|
$this->absoluteUrl($config->caldavUrl, $href),
|
|
$config->caldavUsername,
|
|
$config->caldavPassword,
|
|
['Accept' => 'text/calendar'],
|
|
);
|
|
|
|
if (200 !== $response->statusCode) {
|
|
$this->logger->warning('CalDAV GET by href failed.', [
|
|
'calendarId' => $config->calendarId,
|
|
'url' => $href,
|
|
'statusCode' => $response->statusCode,
|
|
]);
|
|
|
|
return null;
|
|
}
|
|
|
|
return $this->icalendarParser->parseEvent(
|
|
$href,
|
|
trim((string) $response->header('etag'), '"'),
|
|
$response->body,
|
|
$config->timezoneOrDefault(),
|
|
);
|
|
}
|
|
|
|
private function buildCalendarQueryBody(): string
|
|
{
|
|
return <<<'XML'
|
|
<?xml version="1.0" encoding="utf-8" ?>
|
|
<c:calendar-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
|
<d:prop>
|
|
<d:getetag />
|
|
<c:calendar-data />
|
|
</d:prop>
|
|
<c:filter>
|
|
<c:comp-filter name="VCALENDAR">
|
|
<c:comp-filter name="VEVENT" />
|
|
</c:comp-filter>
|
|
</c:filter>
|
|
</c:calendar-query>
|
|
XML;
|
|
}
|
|
|
|
private function absoluteUrl(string $calendarUrl, string $href): string
|
|
{
|
|
if (str_starts_with($href, 'http://') || str_starts_with($href, 'https://')) {
|
|
return $href;
|
|
}
|
|
|
|
$base = parse_url($calendarUrl);
|
|
if (!is_array($base) || !isset($base['scheme'], $base['host'])) {
|
|
return $href;
|
|
}
|
|
|
|
$prefix = $base['scheme'].'://'.$base['host'];
|
|
if (isset($base['port'])) {
|
|
$prefix .= ':'.$base['port'];
|
|
}
|
|
|
|
return $prefix.$href;
|
|
}
|
|
}
|