on('beforeMethod:REPORT', [$this, 'beforeReport'], 0); $server->on('report', [$this, 'report'], 0); } /** * Triggered before any method is handled. * * @return void */ public function beforeReport(RequestInterface $request, ResponseInterface $response) { $userAgent = $request->getRawServerValue('HTTP_USER_AGENT') ?? 'unknown'; $this->isMacOSDavAgent = $this->isMacOSUserAgent($userAgent); } /** * This method handles HTTP REPORT requests. * * @param string $reportName * @param mixed $report * @param mixed $path * * @return bool */ public function report($reportName, $report, $path) { if ($reportName == '{DAV:}principal-property-search' && $this->isMacOSDavAgent) { /** @var \Sabre\DAVACL\Xml\Request\PrincipalPropertySearchReport $report */ $report->applyToPrincipalCollectionSet = true; } return true; } /** * Check whether the given $userAgent string pretends to originate from OSX. * * @param string $userAgent * * @return bool */ protected function isMacOSUserAgent(string $userAgent):bool { return str_starts_with(self::OSX_AGENT_PREFIX, $userAgent); } /** * Decode the given OSX DAV agent string. * * @param string $agent * * @return null|array */ protected function decodeMacOSAgentString(string $userAgent):?array { // OSX agent string is like: macOS/13.2.1 (22D68) dataaccessd/1.0 if (preg_match('|^' . self::OSX_AGENT_PREFIX . '/([0-9]+)\\.([0-9]+)\\.([0-9]+)\s+\((\w+)\)\s+([^/]+)/([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?$|i', $userAgent, $matches)) { return [ 'macOSVersion' => [ 'major' => $matches[1], 'minor' => $matches[2], 'patch' => $matches[3], ], 'macOSAgent' => $matches[5], 'macOSAgentVersion' => [ 'major' => $matches[6], 'minor' => $matches[7] ?? null, 'patch' => $matches[8] ?? null, ], ]; } return null; } }