/** * Sends one or more iTip messages through email. * * @param string $originator Originator Email * @param array $recipients Array of email addresses * @param VObject\Component $vObject * @param string $principal Principal Url of the originator * @return void */ public function sendMessage($originator, array $recipients, VObject\Component $vObject, $principal) { foreach ($recipients as $recipient) { $to = $recipient; $replyTo = $originator; $subject = 'SabreDAV iTIP message'; switch (strtoupper($vObject->METHOD)) { case 'REPLY': $subject = 'Response for: ' . $vObject->VEVENT->SUMMARY; break; case 'REQUEST': $subject = 'Invitation for: ' . $vObject->VEVENT->SUMMARY; break; case 'CANCEL': $subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY; break; } $headers = array(); $headers[] = 'Reply-To: ' . $replyTo; $headers[] = 'From: ' . $this->senderEmail; $headers[] = 'Content-Type: text/calendar; method=' . (string) $vObject->method . '; charset=utf-8'; if (DAV\Server::$exposeVersion) { $headers[] = 'X-Sabre-Version: ' . DAV\Version::VERSION . '-' . DAV\Version::STABILITY; } $vcalBody = $vObject->serialize(); $this->mail($to, $subject, $vcalBody, $headers); } }
private function getValueOrEmpty(\Sabre\VObject\Component $component, $property) { $value = $component->__get($property); if ($value) { return $value->getValue(); } else { return ''; } }
/** * Constructor * * The splitter should receive an readable file stream as it's input. * * @param resource $input */ public function __construct($input) { $data = VObject\Reader::read(stream_get_contents($input)); $vtimezones = array(); $components = array(); foreach ($data->children as $component) { if (!$component instanceof VObject\Component) { continue; } // Get all timezones if ($component->name === 'VTIMEZONE') { $this->vtimezones[(string) $component->TZID] = $component; continue; } // Get component UID for recurring Events search if ($component->UID) { $uid = (string) $component->UID; } else { // Generating a random UID $uid = sha1(microtime()) . '-vobjectimport'; } // Take care of recurring events if (!array_key_exists($uid, $this->objects)) { $this->objects[$uid] = VObject\Component::create('VCALENDAR'); } $this->objects[$uid]->add(clone $component); } }
/** * Different bug, also likely an infinite loop. */ function testYearlyByMonthLoop() { $ev = Component::create('VEVENT'); $ev->UID = 'uuid'; $ev->DTSTART = '20120101T154500'; $ev->DTSTART['TZID'] = 'Europe/Berlin'; $ev->RRULE = 'FREQ=YEARLY;INTERVAL=1;UNTIL=20120203T225959Z;BYMONTH=2;BYSETPOS=1;BYDAY=SU,MO,TU,WE,TH,FR,SA'; $ev->DTEND = '20120101T164500'; $ev->DTEND['TZID'] = 'Europe/Berlin'; // This recurrence rule by itself is a yearly rule that should happen // every february. // // The BYDAY part expands this to every day of the month, but the // BYSETPOS limits this to only the 1st day of the month. Very crazy // way to specify this, and could have certainly been a lot easier. $cal = Component::create('VCALENDAR'); $cal->add($ev); $it = new RecurrenceIterator($cal, 'uuid'); $it->fastForward(new DateTime('2012-01-29 23:00:00', new DateTimeZone('UTC'))); $collect = array(); while ($it->valid()) { $collect[] = $it->getDTSTART(); if ($it->getDTSTART() > new DateTime('2013-02-05 22:59:59', new DateTimeZone('UTC'))) { break; } $it->next(); } $this->assertEquals(array(new DateTime('2012-02-01 15:45:00', new DateTimeZone('Europe/Berlin'))), $collect); }
/** * Serializes a xCal or xCard object. * * @param Component $component * * @return string */ static function writeXml(Component $component) { $writer = new Xml\Writer(); $writer->openMemory(); $writer->setIndent(true); $writer->startDocument('1.0', 'utf-8'); if ($component instanceof Component\VCalendar) { $writer->startElement('icalendar'); $writer->writeAttribute('xmlns', Parser\Xml::XCAL_NAMESPACE); } else { $writer->startElement('vcards'); $writer->writeAttribute('xmlns', Parser\Xml::XCARD_NAMESPACE); } $component->xmlSerialize($writer); $writer->endElement(); return $writer->outputMemory(); }
/** * Validates the node for correctness. * * The following options are supported: * Node::REPAIR - May attempt to automatically repair the problem. * Node::PROFILE_CARDDAV - validate the vCard for CardDAV purposes. * Node::PROFILE_CALDAV - validate the iCalendar for CalDAV purposes. * * This method returns an array with detected problems. * Every element has the following properties: * * * level - problem level. * * message - A human-readable string describing the issue. * * node - A reference to the problematic node. * * The level means: * 1 - The issue was repaired (only happens if REPAIR was turned on). * 2 - A warning. * 3 - An error. * * @param int $options * @return array */ function validate($options = 0) { $result = parent::validate($options); if (isset($this->DTEND) && isset($this->DURATION)) { $result[] = array('level' => 3, 'message' => 'DTEND and DURATION cannot both be present', 'node' => $this); } return $result; }
/** * Validates the node for correctness. * * The following options are supported: * Node::REPAIR - May attempt to automatically repair the problem. * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes. * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes. * * This method returns an array with detected problems. * Every element has the following properties: * * * level - problem level. * * message - A human-readable string describing the issue. * * node - A reference to the problematic node. * * The level means: * 1 - The issue was repaired (only happens if REPAIR was turned on). * 2 - A warning. * 3 - An error. * * @param int $options * * @return array */ function validate($options = 0) { $result = parent::validate($options); if (isset($this->DTEND) && isset($this->DURATION)) { $result[] = ['level' => 3, 'message' => 'DTEND and DURATION cannot both be present', 'node' => $this]; } if (isset($this->DURATION) && !isset($this->DTSTART)) { $result[] = ['level' => 3, 'message' => 'DURATION must be declared with a DTSTART.', 'node' => $this]; } return $result; }
/** * Something, somewhere produced an ics with an interval set to 0. Because * this means we increase the current day (or week, month) by 0, this also * results in an infinite loop. * * @expectedException InvalidArgumentException * @return void */ function testZeroInterval() { $ev = Component::create('VEVENT'); $ev->UID = 'uuid'; $ev->DTSTART = '20120824T145700Z'; $ev->RRULE = 'FREQ=YEARLY;INTERVAL=0'; $cal = Component::create('VCALENDAR'); $cal->add($ev); $it = new RecurrenceIterator($cal, 'uuid'); $it->fastForward(new DateTime('2013-01-01 23:00:00', new DateTimeZone('UTC'))); // if we got this far.. it means we are no longer infinitely looping }
function testAlarmWayBefore() { $vevent = VObject\Component::create('VEVENT'); $vevent->DTSTART = '20120101T120000Z'; $vevent->UID = 'bla'; $valarm = VObject\Component::create('VALARM'); $valarm->TRIGGER = '-P2W1D'; $vevent->add($valarm); $vcalendar = VObject\Component::create('VCALENDAR'); $vcalendar->add($vevent); $filter = array('name' => 'VCALENDAR', 'is-not-defined' => false, 'time-range' => null, 'prop-filters' => array(), 'comp-filters' => array(array('name' => 'VEVENT', 'is-not-defined' => false, 'time-range' => null, 'prop-filters' => array(), 'comp-filters' => array(array('name' => 'VALARM', 'is-not-defined' => false, 'prop-filters' => array(), 'comp-filters' => array(), 'time-range' => array('start' => new DateTime('2011-12-10'), 'end' => new DateTime('2011-12-20'))))))); $validator = new Sabre_CalDAV_CalendarQueryValidator(); $this->assertTrue($validator->validate($vcalendar, $filter)); }
/** * Merges all calendar objects, and builds one big ics export * * @param array $nodes * @return string */ public function generateICS(array $nodes) { $calendar = new VObject\Component('vcalendar'); $calendar->version = '2.0'; if (Sabre_DAV_Server::$exposeVersion) { $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN'; } else { $calendar->prodid = '-//SabreDAV//SabreDAV//EN'; } $calendar->calscale = 'GREGORIAN'; $collectedTimezones = array(); $timezones = array(); $objects = array(); foreach ($nodes as $node) { if (!isset($node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'])) { continue; } $nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data']; $nodeComp = VObject\Reader::read($nodeData); foreach ($nodeComp->children() as $child) { switch ($child->name) { case 'VEVENT': case 'VTODO': case 'VJOURNAL': $objects[] = $child; break; // VTIMEZONE is special, because we need to filter out the duplicates // VTIMEZONE is special, because we need to filter out the duplicates case 'VTIMEZONE': // Naively just checking tzid. if (in_array((string) $child->TZID, $collectedTimezones)) { continue; } $timezones[] = $child; $collectedTimezones[] = $child->TZID; break; } } } foreach ($timezones as $tz) { $calendar->add($tz); } foreach ($objects as $obj) { $calendar->add($obj); } return $calendar->serialize(); }
public function timeRangeTestData() { $tests = array(); $vjournal = Component::create('VJOURNAL'); $vjournal->DTSTART = '20111223T120000Z'; $tests[] = array($vjournal, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vjournal, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vjournal2 = Component::create('VJOURNAL'); $vjournal2->DTSTART = '20111223'; $vjournal2->DTSTART['VALUE'] = 'DATE'; $tests[] = array($vjournal2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vjournal2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vjournal3 = Component::create('VJOURNAL'); $tests[] = array($vjournal3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), false); $tests[] = array($vjournal3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); return $tests; }
/** * @brief transform a ldap entry into an VCard object * for each ldap entry which is like "property: value" * to a VCard entry which is like "PROPERTY[;PARAMETER=param]:value" * @param array $ldap_entry * @return OC_VCard */ public function ldapToVCard($ldapEntry) { $vcard = \Sabre\VObject\Component::create('VCARD'); $vcard->REV = $this->convertDate($ldapEntry['modifytimestamp'][0])->format(\DateTime::W3C); //error_log("modifytimestamp: ".$vcard->REV); $vcard->{'X-LDAP-DN'} = base64_encode($ldapEntry['dn']); // OCP\Util::writeLog('ldap_vcard_connector', __METHOD__.' vcard is '.$vcard->serialize(), \OCP\Util::DEBUG); for ($i = 0; $i < $ldapEntry["count"]; $i++) { // ldap property name : $ldap_entry[$i] $lProperty = $ldapEntry[$i]; for ($j = 0; $j < $ldapEntry[$lProperty]["count"]; $j++) { // What to do : // convert the ldap property into vcard property, type and position (if needed) // $v_params format: array('property' => property, 'type' => array(types), 'position' => position) $v_params = $this->getVCardProperty($lProperty); foreach ($v_params as $v_param) { if (isset($v_param['unassigned'])) { // if the value comes from the unassigned entry, it's a vcard property dumped try { $property = \Sabre\VObject\Reader::read($ldapEntry[$lProperty][$j]); $vcard->add($property); } catch (exception $e) { } } else { // Checks if a same kind of property already exists in the VCard (property and parameters) // if so, sets a property variable with the current data // else, creates a property variable $v_property = $this->getOrCreateVCardProperty($vcard, $v_param, $j); // modify the property with the new data if (strcasecmp($v_param['image'], 'true') == 0) { $this->updateVCardImageProperty($v_property, $ldapEntry[$lProperty][$j], $vcard->VERSION); } else { $this->updateVCardProperty($v_property, $ldapEntry[$lProperty][$j], $v_param['position']); } } } } } if (!isset($vcard->UID)) { $vcard->UID = base64_encode($ldapEntry['dn']); } return $vcard; }
/** * Validates the node for correctness. * * The following options are supported: * - Node::REPAIR - If something is broken, and automatic repair may * be attempted. * * An array is returned with warnings. * * Every item in the array has the following properties: * * level - (number between 1 and 3 with severity information) * * message - (human readable message) * * node - (reference to the offending node) * * @param int $options * @return array */ public function validate($options = 0) { $warnings = array(); $version = $this->select('VERSION'); if (count($version) !== 1) { $warnings[] = array('level' => 1, 'message' => 'The VERSION property must appear in the VCARD component exactly 1 time', 'node' => $this); if ($options & self::REPAIR) { $this->VERSION = self::DEFAULT_VERSION; } } else { $version = (string) $this->VERSION; if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') { $warnings[] = array('level' => 1, 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', 'node' => $this); if ($options & self::REPAIR) { $this->VERSION = '4.0'; } } } $fn = $this->select('FN'); if (count($fn) !== 1) { $warnings[] = array('level' => 1, 'message' => 'The FN property must appear in the VCARD component exactly 1 time', 'node' => $this); if ($options & self::REPAIR && count($fn) === 0) { // We're going to try to see if we can use the contents of the // N property. if (isset($this->N)) { $value = explode(';', (string) $this->N); if (isset($value[1]) && $value[1]) { $this->FN = $value[1] . ' ' . $value[0]; } else { $this->FN = $value[0]; } // Otherwise, the ORG property may work } elseif (isset($this->ORG)) { $this->FN = (string) $this->ORG; } } } return array_merge(parent::validate($options), $warnings); }
/** * Create online calendar for user * * @Route("/{username}.ics") * * @param string $username User to create the calendar for * @return Symfony\Component\HttpFoundation\Response */ public function calendarAction($username) { $user = $this->get('user_provider')->loadUserByUsername($username); $om = $this->getObjectManager('VIB\\FliesBundle\\Entity\\Vial'); $calendar = VObject\Component::create('VCALENDAR'); $calendar->VERSION = '2.0'; $field = 'X-WR-CALNAME'; $calendar->{$field} = $user->getShortName() . '\'s flywork'; $stockDates = $om->getRepository('VIB\\FliesBundle\\Entity\\StockVial')->getFlipDates($user); foreach ($stockDates as $stockDate) { $event = VObject\Component::create('VEVENT'); $calendar->add($event); $event->SUMMARY = 'Transfer stocks'; $dtstart = VObject\Property::create('DTSTART'); $dtstart->setDateTime($stockDate, VObject\Property\DateTime::DATE); $event->DTSTART = $dtstart; $alarm = VObject\Component::create('VALARM'); $event->add($alarm); $alarm->TRIGGER = 'PT8H'; $alarm->ACTION = 'DISPLAY'; } $crossDates = $om->getRepository('VIB\\FliesBundle\\Entity\\CrossVial')->getFlipDates($user); foreach ($crossDates as $crossDate) { $crossDates[] = $crossDate; $event = VObject\Component::create('VEVENT'); $calendar->add($event); $event->SUMMARY = 'Check crosses'; $dtstart = VObject\Property::create('DTSTART'); $dtstart->setDateTime($crossDate, VObject\Property\DateTime::DATE); $event->DTSTART = $dtstart; $alarm = VObject\Component::create('VALARM'); $event->add($alarm); $alarm->TRIGGER = 'PT8H'; $alarm->ACTION = 'DISPLAY'; } return new Response($calendar->serialize(), 200, array('Content-Type' => 'text/calendar; charset=utf-8', 'Content-Disposition' => 'inline; filename="calendar.ics"')); }
public function timeRangeTestData() { $tests = array(); $vtodo = Component::create('VTODO'); $vtodo->DTSTART = '20111223T120000Z'; $tests[] = array($vtodo, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vtodo, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vtodo2 = clone $vtodo; $vtodo2->DURATION = 'P1D'; $tests[] = array($vtodo2, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vtodo2, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vtodo3 = clone $vtodo; $vtodo3->DUE = '20111225'; $tests[] = array($vtodo3, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vtodo3, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vtodo4 = Component::create('VTODO'); $vtodo4->DUE = '20111225'; $tests[] = array($vtodo4, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vtodo4, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vtodo5 = Component::create('VTODO'); $vtodo5->COMPLETED = '20111225'; $tests[] = array($vtodo5, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vtodo5, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vtodo6 = Component::create('VTODO'); $vtodo6->CREATED = '20111225'; $tests[] = array($vtodo6, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vtodo6, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vtodo7 = Component::create('VTODO'); $vtodo7->CREATED = '20111225'; $vtodo7->COMPLETED = '20111226'; $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), false); $vtodo7 = Component::create('VTODO'); $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2012-01-01'), true); $tests[] = array($vtodo7, new \DateTime('2011-01-01'), new \DateTime('2011-11-01'), true); return $tests; }
/** * Reads a property or component from a line. * * @return void */ protected function readProperty($line) { if ($this->options & self::OPTION_FORGIVING) { $propNameToken = 'A-Z0-9\\-\\._\\/'; } else { $propNameToken = 'A-Z0-9\\-\\.'; } $paramNameToken = 'A-Z0-9\\-'; $safeChar = '^";:,'; $qSafeChar = '^"'; $regex = "/\n ^(?P<name> [{$propNameToken}]+ ) (?=[;:]) # property name\n |\n (?<=:)(?P<propValue> .+)\$ # property value\n |\n ;(?P<paramName> [{$paramNameToken}]+) (?=[=;:]) # parameter name\n |\n (=|,)(?P<paramValue> # parameter value\n (?: [{$safeChar}]*) |\n \"(?: [{$qSafeChar}]+)\"\n ) (?=[;:,])\n /xi"; //echo $regex, "\n"; die(); preg_match_all($regex, $line, $matches, PREG_SET_ORDER); $property = ['name' => null, 'parameters' => [], 'value' => null]; $lastParam = null; /** * Looping through all the tokens. * * Note that we are looping through them in reverse order, because if a * sub-pattern matched, the subsequent named patterns will not show up * in the result. */ foreach ($matches as $match) { if (isset($match['paramValue'])) { if ($match['paramValue'] && $match['paramValue'][0] === '"') { $value = substr($match['paramValue'], 1, -1); } else { $value = $match['paramValue']; } $value = $this->unescapeParam($value); if (is_null($lastParam)) { throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions'); } if (is_null($property['parameters'][$lastParam])) { $property['parameters'][$lastParam] = $value; } elseif (is_array($property['parameters'][$lastParam])) { $property['parameters'][$lastParam][] = $value; } else { $property['parameters'][$lastParam] = [$property['parameters'][$lastParam], $value]; } continue; } if (isset($match['paramName'])) { $lastParam = strtoupper($match['paramName']); if (!isset($property['parameters'][$lastParam])) { $property['parameters'][$lastParam] = null; } continue; } if (isset($match['propValue'])) { $property['value'] = $match['propValue']; continue; } if (isset($match['name']) && $match['name']) { $property['name'] = strtoupper($match['name']); continue; } // @codeCoverageIgnoreStart throw new \LogicException('This code should not be reachable'); // @codeCoverageIgnoreEnd } if (is_null($property['value'])) { $property['value'] = ''; } if (!$property['name']) { if ($this->options & self::OPTION_IGNORE_INVALID_LINES) { return false; } throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions'); } // vCard 2.1 states that parameters may appear without a name, and only // a value. We can deduce the value based on it's name. // // Our parser will get those as parameters without a value instead, so // we're filtering these parameters out first. $namedParameters = []; $namelessParameters = []; foreach ($property['parameters'] as $name => $value) { if (!is_null($value)) { $namedParameters[$name] = $value; } else { $namelessParameters[] = $name; } } $propObj = $this->root->createProperty($property['name'], null, $namedParameters); foreach ($namelessParameters as $namelessParameter) { $propObj->add(null, $namelessParameter); } if (strtoupper($propObj['ENCODING']) === 'QUOTED-PRINTABLE') { $propObj->setQuotedPrintableValue($this->extractQuotedPrintableValue()); } else { $propObj->setRawMimeDirValue($property['value']); } return $propObj; }
/** * add photo data to VCard * * @param Addressbook_Model_Contact $record * @param \Sabre\VObject\Component $card */ protected function _fromTine20ModelAddPhoto(Addressbook_Model_Contact $record, \Sabre\VObject\Component $card) { if (!empty($record->jpegphoto)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__); try { $jpegData = $record->getSmallContactImage($this->_maxPhotoSize); $card->add('PHOTO', $jpegData, array('TYPE' => 'JPEG', 'ENCODING' => 'b')); } catch (Exception $e) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Image for contact {$record->getId()} not found or invalid: {$e->getMessage()}"); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $e->getTraceAsString()); } } } }
/** * @brief converts a ldif into a owncloud VCard * @param $element the VCard element to convert * @return VCard */ public function convertElementToVCard($element) { $dest = \Sabre\VObject\Component::create('VCARD'); foreach ($element as $ldifProperty) { $importEntry = $this->getImportEntry($ldifProperty[0]); if ($importEntry) { $value = $ldifProperty[1]; if (isset($importEntry['remove'])) { $value = str_replace($importEntry['remove'], '', $ldifProperty[1]); } $values = array($value); if (isset($importEntry['separator'])) { $values = explode($importEntry['separator'], $value); } foreach ($values as $oneValue) { $this->convertElementToProperty($oneValue, $importEntry, $dest); } } else { $property = \Sabre\VObject\Property::create("X-Unknown-Element", ''.StringUtil::convertToUTF8($ldifProperty[1])); $property->parameters[] = new \Sabre\VObject\Parameter('TYPE', ''.StringUtil::convertToUTF8($ldifProperty[0])); $dest->add($property); } } $dest->validate(\Sabre\VObject\Component\VCard::REPAIR); return $dest; }
/** * Returns free-busy information for a specific address. The returned * data is an array containing the following properties: * * calendar-data : A VFREEBUSY VObject * request-status : an iTip status code. * href: The principal's email address, as requested * * The following request status codes may be returned: * * 2.0;description * * 3.7;description * * @param string $email address * @param \DateTime $start * @param \DateTime $end * @param VObject\Component $request * @return array */ protected function getFreeBusyForEmail($email, \DateTime $start, \DateTime $end, VObject\Component $request) { $caldavNS = '{' . Plugin::NS_CALDAV . '}'; $aclPlugin = $this->server->getPlugin('acl'); if (substr($email, 0, 7) === 'mailto:') { $email = substr($email, 7); } $result = $aclPlugin->principalSearch(array('{http://sabredav.org/ns}email-address' => $email), array('{DAV:}principal-URL', $caldavNS . 'calendar-home-set', '{http://sabredav.org/ns}email-address')); if (!count($result)) { return array('request-status' => '3.7;Could not find principal', 'href' => 'mailto:' . $email); } if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { return array('request-status' => '3.7;No calendar-home-set property found', 'href' => 'mailto:' . $email); } $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); // Grabbing the calendar list $objects = array(); foreach ($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) { if (!$node instanceof ICalendar) { continue; } $aclPlugin->checkPrivileges($homeSet . $node->getName(), $caldavNS . 'read-free-busy'); // Getting the list of object uris within the time-range $urls = $node->calendarQuery(array('name' => 'VCALENDAR', 'comp-filters' => array(array('name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array('start' => $start, 'end' => $end))), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null)); $calObjects = array_map(function ($url) use($node) { $obj = $node->getChild($url)->get(); return $obj; }, $urls); $objects = array_merge($objects, $calObjects); } $vcalendar = VObject\Component::create('VCALENDAR'); $vcalendar->VERSION = '2.0'; $vcalendar->METHOD = 'REPLY'; $vcalendar->CALSCALE = 'GREGORIAN'; $vcalendar->PRODID = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; $generator = new VObject\FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $generator->setBaseObject($vcalendar); $result = $generator->getResult(); $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; $vcalendar->VFREEBUSY->UID = (string) $request->VFREEBUSY->UID; $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; return array('calendar-data' => $result, 'request-status' => '2.0;Success', 'href' => 'mailto:' . $email); }
public static function getBirthdayEvents($parameters) { $name = $parameters['calendar_id']; if (strpos($name, 'birthday_') != 0) { return; } $info = explode('_', $name); $aid = $info[1]; Addressbook::find($aid); foreach (VCard::all($aid) as $contact) { try { $vcard = VObject\Reader::read($contact['carddata']); } catch (Exception $e) { continue; } $birthday = $vcard->BDAY; if ($birthday) { $date = new \DateTime($birthday); $vevent = VObject\Component::create('VEVENT'); //$vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV)); $vevent->add('DTSTART'); $vevent->DTSTART->setDateTime($date, VObject\Property\DateTime::DATE); $vevent->add('DURATION', 'P1D'); $vevent->{'UID'} = substr(md5(rand() . time()), 0, 10); // DESCRIPTION? $vevent->{'RRULE'} = 'FREQ=YEARLY'; $title = str_replace('{name}', $vcard->FN, App::$l10n->t('{name}\'s Birthday')); $parameters['events'][] = array('id' => 0, 'vevent' => $vevent, 'repeating' => true, 'summary' => $title, 'calendardata' => "BEGIN:VCALENDAR\nVERSION:2.0\n" . "PRODID:ownCloud Contacts " . \OCP\App::getAppVersion('contacts') . "\n" . $vevent->serialize() . "END:VCALENDAR"); } } }
/** * @brief converts a VCard into a owncloud VCard * @param $element the VCard element to convert * @return VCard|false */ public function convertElementToVCard($element) { try { $source = VObject\Reader::read($element, VObject\Reader::OPTION_FORGIVING); } catch (VObject\ParseException $error) { return false; } $dest = \Sabre\VObject\Component::create('VCARD'); foreach ($source->children() as $sourceProperty) { $importEntry = $this->getImportEntry($sourceProperty, $source); if ($importEntry) { $value = $sourceProperty->value; if (isset($importEntry['remove'])) { $value = str_replace($importEntry['remove'], '', $sourceProperty->value); } $values = array($value); if (isset($importEntry['separator'])) { $values = explode($importEntry['separator'], $value); } foreach ($values as $oneValue) { if (isset($importEntry->vcard_favourites)) { foreach ($importEntry->vcard_favourites as $vcardFavourite) { if (strcasecmp((string)$vcardFavourite, trim($oneValue)) == 0) { $property = \Sabre\VObject\Property::create("X-FAVOURITES", 'yes'); $dest->add($property); } else { $property = $this->getOrCreateVCardProperty($dest, $importEntry->vcard_entry); $this->updateProperty($property, $importEntry, trim($oneValue)); } } } else { $property = $this->getOrCreateVCardProperty($dest, $importEntry->vcard_entry); $this->updateProperty($property, $importEntry, $sourceProperty->value); } } } else { $property = clone $sourceProperty; $dest->add($property); } } $dest->validate(\Sabre\VObject\Component\VCard::REPAIR); return $dest; }
/** * Returns a VTIMEZONE component for a Olson timezone identifier * with daylight transitions covering the given date range. * * @param string Timezone ID as used in PHP's Date functions * @param integer Unix timestamp with first date/time in this timezone * @param integer Unix timestap with last date/time in this timezone * * @return mixed A Sabre\VObject\Component object representing a VTIMEZONE definition * or false if no timezone information is available */ public static function get_vtimezone($tzid, $from = 0, $to = 0) { if (!$from) { $from = time(); } if (!$to) { $to = $from; } if (is_string($tzid)) { try { $tz = new \DateTimeZone($tzid); } catch (\Exception $e) { return false; } } else { if (is_a($tzid, '\\DateTimeZone')) { $tz = $tzid; } } if (!is_a($tz, '\\DateTimeZone')) { return false; } $year = 86400 * 360; $transitions = $tz->getTransitions($from - $year, $to + $year); $vt = new VObject\Component('VTIMEZONE'); $vt->TZID = $tz->getName(); $std = null; $dst = null; foreach ($transitions as $i => $trans) { $cmp = null; if ($i == 0) { $tzfrom = $trans['offset'] / 3600; continue; } if ($trans['isdst']) { $t_dst = $trans['ts']; $dst = new VObject\Component('DAYLIGHT'); $cmp = $dst; } else { $t_std = $trans['ts']; $std = new VObject\Component('STANDARD'); $cmp = $std; } if ($cmp) { $dt = new DateTime($trans['time']); $offset = $trans['offset'] / 3600; $cmp->DTSTART = $dt->format('Ymd\\THis'); $cmp->TZOFFSETFROM = sprintf('%s%02d%02d', $tzfrom >= 0 ? '+' : '', floor($tzfrom), ($tzfrom - floor($tzfrom)) * 60); $cmp->TZOFFSETTO = sprintf('%s%02d%02d', $offset >= 0 ? '+' : '', floor($offset), ($offset - floor($offset)) * 60); if (!empty($trans['abbr'])) { $cmp->TZNAME = $trans['abbr']; } $tzfrom = $offset; $vt->add($cmp); } // we covered the entire date range if ($std && $dst && min($t_std, $t_dst) < $from && max($t_std, $t_dst) > $to) { break; } } // add X-MICROSOFT-CDO-TZID if available $microsoftExchangeMap = array_flip(VObject\TimeZoneUtil::$microsoftExchangeMap); if (array_key_exists($tz->getName(), $microsoftExchangeMap)) { $vt->add('X-MICROSOFT-CDO-TZID', $microsoftExchangeMap[$tz->getName()]); } return $vt; }
/** * Repairs a VObject file * * @param Component $vObj * @return int */ protected function repair($vObj) { $returnCode = 0; switch ($vObj->name) { case 'VCALENDAR': $this->log("iCalendar: " . (string) $vObj->VERSION); break; case 'VCARD': $this->log("vCard: " . (string) $vObj->VERSION); break; } $warnings = $vObj->validate(Node::REPAIR); if (!count($warnings)) { $this->log(" No warnings!"); } else { $levels = [1 => 'REPAIRED', 2 => 'WARNING', 3 => 'ERROR']; $returnCode = 2; foreach ($warnings as $warn) { $extra = ''; if ($warn['node'] instanceof Property) { $extra = ' (property: "' . $warn['node']->name . '")'; } $this->log(" [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra); } } fwrite($this->stdout, $vObj->serialize()); return $returnCode; }
protected function serializeComponent(Component $vObj) { $this->cWrite('cyan', 'BEGIN'); $this->cWrite('red', ':'); $this->cWrite('yellow', $vObj->name . "\n"); /** * Gives a component a 'score' for sorting purposes. * * This is solely used by the childrenSort method. * * A higher score means the item will be lower in the list. * To avoid score collisions, each "score category" has a reasonable * space to accomodate elements. The $key is added to the $score to * preserve the original relative order of elements. * * @param int $key * @param array $array * * @return int */ $sortScore = function ($key, $array) { if ($array[$key] instanceof Component) { // We want to encode VTIMEZONE first, this is a personal // preference. if ($array[$key]->name === 'VTIMEZONE') { $score = 300000000; return $score + $key; } else { $score = 400000000; return $score + $key; } } else { // Properties get encoded first // VCARD version 4.0 wants the VERSION property to appear first if ($array[$key] instanceof Property) { if ($array[$key]->name === 'VERSION') { $score = 100000000; return $score + $key; } else { // All other properties $score = 200000000; return $score + $key; } } } }; $children = $vObj->children(); $tmp = $children; uksort($children, function ($a, $b) use($sortScore, $tmp) { $sA = $sortScore($a, $tmp); $sB = $sortScore($b, $tmp); return $sA - $sB; }); foreach ($children as $child) { if ($child instanceof Component) { $this->serializeComponent($child); } else { $this->serializeProperty($child); } } $this->cWrite('cyan', 'END'); $this->cWrite('red', ':'); $this->cWrite('yellow', $vObj->name . "\n"); }
/** * Create a property. * * @param Component $parentComponent * @param string $name * @param array $parameters * @param string $type * @param mixed $value * * @return void */ protected function createProperty(Component $parentComponent, $name, $parameters, $type, $value) { $property = $this->root->createProperty($name, null, $parameters, $type); $parentComponent->add($property); $property->setXmlValue($value); }
/** * @param $properties * @return mixed */ public function createOrUpdate($properties) { $id = null; /** * @var \OCA\Contacts\VObject\VCard */ $vcard = null; if (array_key_exists('id', $properties)) { // TODO: test if $id belongs to this addressbook $id = $properties['id']; // TODO: Test $vcard $vcard = $this->addressBook->getChild($properties['id']); foreach (array_keys($properties) as $name) { if (isset($vcard->{$name})) { unset($vcard->{$name}); } } } else { $vcard = \Sabre\VObject\Component::create('VCARD'); $uid = substr(md5(rand() . time()), 0, 10); $vcard->add('UID', $uid); try { $id = $this->addressBook->addChild($vcard); } catch (\Exception $e) { \OCP\Util::writeLog('contacts', __METHOD__ . ' ' . $e->getMessage(), \OCP\Util::ERROR); return false; } } foreach ($properties as $name => $value) { switch ($name) { case 'ADR': case 'N': if (is_array($value)) { $property = \Sabre\VObject\Property::create($name); $property->setParts($value); $vcard->add($property); } else { $vcard->{$name} = $value; } break; case 'BDAY': // TODO: try/catch $date = new \DateTime($value); $vcard->BDAY = $date->format('Y-m-d'); $vcard->BDAY->VALUE = 'DATE'; break; case 'EMAIL': case 'TEL': case 'IMPP': // NOTE: We don't know if it's GTalk, Jabber etc. only the protocol // NOTE: We don't know if it's GTalk, Jabber etc. only the protocol case 'URL': if (is_array($value)) { foreach ($value as $val) { $vcard->add($name, strip_tags($val)); } } else { $vcard->add($name, strip_tags($value)); } default: $vcard->{$name} = $value; break; } } try { VCard::edit($id, $vcard); } catch (\Exception $e) { \OCP\Util::writeLog('contacts', __METHOD__ . ' ' . $e->getMessage(), \OCP\Util::ERROR); return false; } $asarray = VCard::structureContact($vcard); $asarray['id'] = $id; return $asarray; }
/** * Validates the node for correctness. * * The following options are supported: * Node::REPAIR - May attempt to automatically repair the problem. * * This method returns an array with detected problems. * Every element has the following properties: * * * level - problem level. * * message - A human-readable string describing the issue. * * node - A reference to the problematic node. * * The level means: * 1 - The issue was repaired (only happens if REPAIR was turned on) * 2 - An inconsequential issue * 3 - A severe issue. * * @param int $options * @return array */ public function validate($options = 0) { $result = parent::validate($options); if (isset($this->DUE) && isset($this->DTSTART)) { $due = $this->DUE; $dtStart = $this->DTSTART; if ($due->getValueType() !== $dtStart->getValueType()) { $result[] = array('level' => 3, 'message' => 'The value type (DATE or DATE-TIME) must be identical for DUE and DTSTART', 'node' => $due); } elseif ($due->getDateTime() < $dtStart->getDateTime()) { $result[] = array('level' => 3, 'message' => 'DUE must occur after DTSTART', 'node' => $due); } } return $result; }
/** * @expectedException LogicException */ public function testInTimeRangeInvalidComponent() { $valarm = Component::create('VALARM'); $valarm->TRIGGER = '-P1D'; $valarm->TRIGGER['RELATED'] = 'END'; $vjournal = Component::create('VJOURNAL'); $vjournal->add($valarm); $valarm->isInTimeRange(new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00')); }
/** * Generate an event to show in the calendar * * @return \Sabre\VObject\Component\VCalendar|null */ public function getBirthdayEvent() { if (!isset($this->BDAY)) { return; } $birthday = $this->BDAY; if ((string) $birthday) { $title = str_replace('{name}', strtr((string) $this->FN, array('\\,' => ',', '\\;' => ';')), App::$l10n->t('{name}\'s Birthday')); try { $date = new \DateTime($birthday); } catch (\Exception $e) { continue; } $vevent = \Sabre\VObject\Component::create('VEVENT'); $vevent->add('DTSTART'); $vevent->DTSTART->setDateTime($date, \Sabre\VObject\Property\DateTime::DATE); $vevent->add('DURATION', 'P1D'); $vevent->{'UID'} = $this->UID; $vevent->{'RRULE'} = 'FREQ=YEARLY'; $vevent->{'SUMMARY'} = $title; $vcal = \Sabre\VObject\Component::create('VCALENDAR'); $vcal->VERSION = '2.0'; $appinfo = \OCP\App::getAppInfo('contacts'); $appversion = \OCP\App::getAppVersion('contacts'); $vcal->PRODID = '-//ownCloud//NONSGML ' . $appinfo['name'] . ' ' . $appversion . '//EN'; $vcal->add($vevent); return $vcal; } }
/** * Repairs a VObject file * * @param Component $vObj * @return int */ protected function repair($vObj) { $returnCode = 0; switch ($vObj->name) { case 'VCALENDAR': $this->log("iCalendar: " . (string) $vObj->VERSION); break; case 'VCARD': $this->log("vCard: " . (string) $vObj->VERSION); break; } $warnings = $vObj->validate(Node::REPAIR); if (!count($warnings)) { $this->log(" No warnings!"); } else { foreach ($warnings as $warn) { $returnCode = 2; $this->log(" " . $warn['message']); } } fwrite($this->stdout, $vObj->serialize()); return $returnCode; }