/** * This method serializes the entire notification, as it is used in the * response body. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ public function serializeBody(DAV\Server $server, \DOMElement $node) { $doc = $node->ownerDocument; $dt = $doc->createElement('cs:dtstamp'); $this->dtStamp->setTimezone(new \DateTimezone('GMT')); $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); $node->appendChild($dt); $prop = $doc->createElement('cs:invite-notification'); $node->appendChild($prop); $uid = $doc->createElement('cs:uid'); $uid->appendChild($doc->createTextNode($this->id)); $prop->appendChild($uid); $href = $doc->createElement('d:href'); $href->appendChild($doc->createTextNode($this->href)); $prop->appendChild($href); $nodeName = null; switch ($this->type) { case SharingPlugin::STATUS_ACCEPTED: $nodeName = 'cs:invite-accepted'; break; case SharingPlugin::STATUS_DECLINED: $nodeName = 'cs:invite-declined'; break; case SharingPlugin::STATUS_DELETED: $nodeName = 'cs:invite-deleted'; break; case SharingPlugin::STATUS_NORESPONSE: $nodeName = 'cs:invite-noresponse'; break; } $prop->appendChild($doc->createElement($nodeName)); $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); $hostUrl = $doc->createElement('cs:hosturl'); $hostUrl->appendChild($hostHref); $prop->appendChild($hostUrl); $access = $doc->createElement('cs:access'); if ($this->readOnly) { $access->appendChild($doc->createElement('cs:read')); } else { $access->appendChild($doc->createElement('cs:read-write')); } $prop->appendChild($access); $organizerUrl = $doc->createElement('cs:organizer'); // If the organizer contains a 'mailto:' part, it means it should be // treated as absolute. if (strtolower(substr($this->organizer, 0, 7)) === 'mailto:') { $organizerHref = new DAV\Property\Href($this->organizer, false); } else { $organizerHref = new DAV\Property\Href($this->organizer, true); } $organizerHref->serialize($server, $organizerUrl); if ($this->commonName) { $commonName = $doc->createElement('cs:common-name'); $commonName->appendChild($doc->createTextNode($this->commonName)); $organizerUrl->appendChild($commonName); $commonNameOld = $doc->createElement('cs:organizer-cn'); $commonNameOld->appendChild($doc->createTextNode($this->commonName)); $prop->appendChild($commonNameOld); } if ($this->firstName) { $firstName = $doc->createElement('cs:first-name'); $firstName->appendChild($doc->createTextNode($this->firstName)); $organizerUrl->appendChild($firstName); $firstNameOld = $doc->createElement('cs:organizer-first'); $firstNameOld->appendChild($doc->createTextNode($this->firstName)); $prop->appendChild($firstNameOld); } if ($this->lastName) { $lastName = $doc->createElement('cs:last-name'); $lastName->appendChild($doc->createTextNode($this->lastName)); $organizerUrl->appendChild($lastName); $lastNameOld = $doc->createElement('cs:organizer-last'); $lastNameOld->appendChild($doc->createTextNode($this->lastName)); $prop->appendChild($lastNameOld); } $prop->appendChild($organizerUrl); if ($this->summary) { $summary = $doc->createElement('cs:summary'); $summary->appendChild($doc->createTextNode($this->summary)); $prop->appendChild($summary); } if ($this->supportedComponents) { $xcomp = $doc->createElement('cal:supported-calendar-component-set'); $this->supportedComponents->serialize($server, $xcomp); $prop->appendChild($xcomp); } }
/** * This event is triggered when the server didn't know how to handle a * certain request. * * We intercept this to handle POST requests on calendars. * * @param string $method * @param string $uri * @return null|bool */ public function unknownMethod($method, $uri) { if ($method !== 'POST') { return; } // Only handling xml $contentType = $this->server->httpRequest->getHeader('Content-Type'); if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false) { return; } // Making sure the node exists try { $node = $this->server->tree->getNodeForPath($uri); } catch (DAV\Exception\NotFound $e) { return; } $requestBody = $this->server->httpRequest->getBody(true); // If this request handler could not deal with this POST request, it // will return 'null' and other plugins get a chance to handle the // request. // // However, we already requested the full body. This is a problem, // because a body can only be read once. This is why we preemptively // re-populated the request body with the existing data. $this->server->httpRequest->setBody($requestBody); $dom = DAV\XMLUtil::loadDOMDocument($requestBody); $documentType = DAV\XMLUtil::toClarkNotation($dom->firstChild); switch ($documentType) { // Dealing with the 'share' document, which modified invitees on a // calendar. case '{' . Plugin::NS_CALENDARSERVER . '}share': // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($uri, '{DAV:}write'); } $mutations = $this->parseShareRequest($dom); $node->updateShares($mutations[0], $mutations[1]); $this->server->httpResponse->sendStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; // The invite-reply document is sent when the user replies to an // invitation of a calendar share. // The invite-reply document is sent when the user replies to an // invitation of a calendar share. case '{' . Plugin::NS_CALENDARSERVER . '}invite-reply': // This only works on the calendar-home-root node. if (!$node instanceof UserCalendars) { return; } // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($uri, '{DAV:}write'); } $message = $this->parseInviteReplyRequest($dom); $url = $node->shareReply($message['href'], $message['status'], $message['calendarUri'], $message['inReplyTo'], $message['summary']); $this->server->httpResponse->sendStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); if ($url) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $root = $dom->createElement('cs:shared-as'); foreach ($this->server->xmlNamespaces as $namespace => $prefix) { $root->setAttribute('xmlns:' . $prefix, $namespace); } $dom->appendChild($root); $href = new DAV\Property\Href($url); $href->serialize($this->server, $root); $this->server->httpResponse->setHeader('Content-Type', 'application/xml'); $this->server->httpResponse->sendBody($dom->saveXML()); } // Breaking the event chain return false; case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar': // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($uri, '{DAV:}write'); } $node->setPublishStatus(true); // iCloud sends back the 202, so we will too. $this->server->httpResponse->sendStatus(202); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar': // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($uri, '{DAV:}write'); } $node->setPublishStatus(false); $this->server->httpResponse->sendStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; } }