/** * Updates the VCard-formatted object * * @param string $cardData * @return void */ public function put($cardData) { if (is_resource($cardData)) { $cardData = stream_get_contents($cardData); } // Converting to UTF-8, if needed $cardData = Sabre_DAV_StringUtil::ensureUTF8($cardData); $this->carddavBackend->updateCard($this->addressBookInfo['id'], $this->cardData['uri'], $cardData); $this->cardData['carddata'] = $cardData; }
/** * Updates the ICalendar-formatted object * * @param string $calendarData * @return void */ public function put($calendarData) { if (is_resource($calendarData)) { $calendarData = stream_get_contents($calendarData); } // Converting to UTF-8, if needed $calendarData = Sabre_DAV_StringUtil::ensureUTF8($calendarData); $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']; if ($supportedComponents) { $supportedComponents = $supportedComponents->getValue(); } else { $supportedComponents = null; } Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents); $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'], $this->objectData['uri'], $calendarData); $this->objectData['calendardata'] = $calendarData; }
/** * Checks if the submitted vCard data is in fact, valid. * * An exception is thrown if it's not. * * @param resource|string $data * @return void */ protected function validateVCard(&$data) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } // Converting the data to unicode, if needed. $data = \Sabre_DAV_StringUtil::ensureUTF8($data); //\OCP\Util::writeLog('contacts', __METHOD__ . "\n".$data, \OCP\Util::DEBUG); try { $vobj = VObject\Reader::read($data, VObject\Reader::OPTION_IGNORE_INVALID_LINES); } catch (VObject\ParseException $e) { throw new \Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage()); } if ($vobj->name !== 'VCARD') { throw new \Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.'); } $vobj->validate(VCard::REPAIR | VCard::UPGRADE); $data = $vobj->serialize(); }
/** * Validates if a text-filter can be applied to a specific property. * * @param array $texts * @param array $filters * @param string $test * @return bool */ protected function validateTextMatches(array $texts, array $filters, $test) { foreach ($filters as $filter) { $success = false; foreach ($texts as $haystack) { $success = Sabre_DAV_StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']); // Breaking on the first match if ($success) { break; } } if ($filter['negate-condition']) { $success = !$success; } if ($success && $test === 'anyof') { return true; } if (!$success && $test == 'allof') { return false; } } // If we got all the way here, it means we haven't been able to // determine early if the test failed or not. // // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. return $test === 'allof'; }
/** * Creates a new file * * The contents of the new file must be a valid VCARD * * @param string $name * @param resource $vcardData * @return void */ public function createFile($name, $vcardData = null) { $vcardData = stream_get_contents($vcardData); // Converting to UTF-8, if needed $vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData); $this->carddavBackend->createCard($this->addressBookInfo['id'], $name, $vcardData); }
/** * Checks if the submitted iCalendar data is in fact, valid. * * An exception is thrown if it's not. * * @param resource|string $data * @return void */ protected function validateICalendar(&$data) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } // Converting the data to unicode, if needed. $data = Sabre_DAV_StringUtil::ensureUTF8($data); try { $vobj = Sabre_VObject_Reader::read($data); } catch (Sabre_VObject_ParseException $e) { throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); } if ($vobj->name !== 'VCALENDAR') { throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.'); } $foundType = null; $foundUID = null; foreach ($vobj->getComponents() as $component) { switch ($component->name) { case 'VTIMEZONE': continue 2; case 'VEVENT': case 'VTODO': case 'VJOURNAL': if (is_null($foundType)) { $foundType = $component->name; if (!isset($component->UID)) { throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID'); } $foundUID = (string) $component->UID; } else { if ($foundType !== $component->name) { throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType); } if ($foundUID !== (string) $component->UID) { throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs'); } } break; default: throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here'); } } if (!$foundType) { throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL'); } }
/** * Verify if a list of filters applies to the calendar data object * * The calendarData object must be a valid iCalendar blob. The list of * filters must be formatted as parsed by Sabre_CalDAV_Plugin::parseCalendarQueryFilters * * @param string $calendarData * @param array $filters * @return bool */ public function validateFilters($calendarData, $filters) { // We are converting the calendar object to an XML structure // This makes it far easier to parse $xCalendarData = Sabre_CalDAV_ICalendarUtil::toXCal($calendarData); $xml = simplexml_load_string($xCalendarData); $xml->registerXPathNamespace('c', 'urn:ietf:params:xml:ns:xcal'); foreach ($filters as $xpath => $filter) { // if-not-defined comes first if (isset($filter['is-not-defined'])) { if (!$xml->xpath($xpath)) { continue; } else { return false; } } $elem = $xml->xpath($xpath); if (!$elem) { return false; } $elem = $elem[0]; if (isset($filter['time-range'])) { switch ($elem->getName()) { case 'vevent': $result = $this->validateTimeRangeFilterForEvent($xml, $xpath, $filter); if ($result === false) { return false; } break; case 'vtodo': $result = $this->validateTimeRangeFilterForTodo($xml, $xpath, $filter); if ($result === false) { return false; } break; case 'vjournal': case 'vfreebusy': case 'valarm': // TODO: not implemented break; /* case 'vjournal' : $result = $this->validateTimeRangeFilterForJournal($xml,$xpath,$filter); if ($result===false) return false; break; case 'vfreebusy' : $result = $this->validateTimeRangeFilterForFreeBusy($xml,$xpath,$filter); if ($result===false) return false; break; case 'valarm' : $result = $this->validateTimeRangeFilterForAlarm($xml,$xpath,$filter); if ($result===false) return false; break; */ } } if (isset($filter['text-match'])) { $currentString = (string) $elem; $isMatching = Sabre_DAV_StringUtil::textMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']); if ($filter['text-match']['negate-condition'] && $isMatching) { return false; } if (!$filter['text-match']['negate-condition'] && !$isMatching) { return false; } } } return true; }
/** * Checks if the submitted iCalendar data is in fact, valid. * * An exception is thrown if it's not. * * @param resource|string $data * @return void */ protected function validateICalendar(&$data) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } // Converting the data to unicode, if needed. $data = Sabre_DAV_StringUtil::ensureUTF8($data); try { $vobj = Sabre_VObject_Reader::read($data); } catch (Sabre_VObject_ParseException $e) { throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); } }
/** * This method checks the validity of a text-match. * * A single text-match should be specified as well as the specific property * or parameter we need to validate. * * @param Sabre_VObject_Node $parent * @param array $textMatch * @return bool */ protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) { $value = (string) $parent; $isMatching = Sabre_DAV_StringUtil::textMatch($value, $textMatch['value'], $textMatch['collation']); return $textMatch['negate-condition'] xor $isMatching; }
/** * Checks if the submitted iCalendar data is in fact, valid. * * An exception is thrown if it's not. * * @param resource|string $data * @param string $path * @return void */ protected function validateICalendar(&$data, $path) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } // Converting the data to unicode, if needed. $data = Sabre_DAV_StringUtil::ensureUTF8($data); try { $vobj = VObject\Reader::read($data); } catch (VObject\ParseException $e) { throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); } if ($vobj->name !== 'VCALENDAR') { throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.'); } // Get the Supported Components for the target calendar list($parentPath, $object) = Sabre_Dav_URLUtil::splitPath($path); $calendarProperties = $this->server->getProperties($parentPath, array('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set')); $supportedComponents = $calendarProperties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue(); $foundType = null; $foundUID = null; foreach ($vobj->getComponents() as $component) { switch ($component->name) { case 'VTIMEZONE': continue 2; case 'VEVENT': case 'VTODO': case 'VJOURNAL': if (is_null($foundType)) { $foundType = $component->name; if (!in_array($foundType, $supportedComponents)) { throw new Sabre_CalDAV_Exception_InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType); } if (!isset($component->UID)) { throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID'); } $foundUID = (string) $component->UID; } else { if ($foundType !== $component->name) { throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType); } if ($foundUID !== (string) $component->UID) { throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs'); } } break; default: throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here'); } } if (!$foundType) { throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL'); } }
/** * Creates a new file * * The contents of the new file must be a valid ICalendar string. * * @param string $name * @param resource $calendarData * @return void */ public function createFile($name, $calendarData = null) { $calendarData = stream_get_contents($calendarData); // Converting to UTF-8, if needed $calendarData = Sabre_DAV_StringUtil::ensureUTF8($calendarData); $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']; if ($supportedComponents) { $supportedComponents = $supportedComponents->getValue(); } else { $supportedComponents = null; } Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents); $this->caldavBackend->createCalendarObject($this->calendarInfo['id'], $name, $calendarData); }
/** * Updates the VCard-formatted object * * @param string $cardData * @return void */ public function put($cardData) { if (get_class($this->_converter) == 'Calendar_Convert_Event_VCalendar_Generic') { if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) { Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " update by generic client not allowed. See Calendat_Convert_Event_VCalendar_Factory for supported clients."); } throw new Sabre_DAV_Exception_Forbidden('Update denied for unknow client'); } if (is_resource($cardData)) { $cardData = stream_get_contents($cardData); } // Converting to UTF-8, if needed $cardData = Sabre_DAV_StringUtil::ensureUTF8($cardData); Sabre_CalDAV_ICalendarUtil::validateICalendarObject($cardData, array('VEVENT', 'VFREEBUSY')); $vobject = Calendar_Convert_Event_VCalendar_Abstract::getVcal($cardData); foreach ($vobject->children() as $component) { if (isset($component->{'X-TINE20-CONTAINER'})) { $xContainerId = $component->{'X-TINE20-CONTAINER'}; break; } } // keep old record for reference $recordBeforeUpdate = clone $this->getRecord(); $event = $this->_converter->toTine20Model($vobject, $this->getRecord()); // iCal does sends back an old value, because it does not refresh the vcalendar after // update. Therefor we must reapply the value of last_modified_time after the convert $event->last_modified_time = $recordBeforeUpdate->last_modified_time; $currentContainer = Tinebase_Container::getInstance()->getContainerById($this->getRecord()->container_id); $ownAttendee = Calendar_Model_Attender::getOwnAttender($this->getRecord()->attendee); // event 'belongs' current user -> allow container move if ($currentContainer->isPersonalOf(Tinebase_Core::getUser())) { $event->container_id = $this->_container->getId(); } else { if (isset($xContainerId)) { if ($xContainerId == $currentContainer->getId()) { $event->container_id = $this->_container->getId(); } else { // @TODO allow organizer to move original cal when he edits the displaycal event? if ($ownAttendee && $this->_container->type == Tinebase_Model_Container::TYPE_PERSONAL) { $ownAttendee->displaycontainer_id = $this->_container->getId(); } } } else { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " X-TINE20-CONTAINER not present -> restrict container moves"); } if ($ownAttendee && $this->_container->type == Tinebase_Model_Container::TYPE_PERSONAL) { if ($ownAttendee->displaycontainer_id == $currentContainer->getId()) { $event->container_id = $this->_container->getId(); } $ownAttendee->displaycontainer_id = $this->_container->getId(); } } } self::enforceEventParameters($event); // don't allow update of alarms for non organizer if oganizer is Tine 2.0 user if ($event->organizer !== Tinebase_Core::getUser()->contact_id) { $organizerContact = Addressbook_Controller_Contact::getInstance()->get($event->organizer); // reset alarms if organizer is Tine 2.0 user if (!empty($organizerContact->account_id)) { $this->_resetAlarms($event, $recordBeforeUpdate); } } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . print_r($event->toArray(), true)); } try { $this->_event = Calendar_Controller_MSEventFacade::getInstance()->update($event); } catch (Tinebase_Timemachine_Exception_ConcurrencyConflict $ttecc) { throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.', 'If-Match'); } // avoid sending headers during unit tests if (php_sapi_name() != 'cli') { // @todo this belong to DAV_Server, but it currently not supported header('ETag: ' . $this->getETag()); } }