Release: CalDAV sync bundle hardening and LMW sync
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user