/** */ protected function _create($mbox, $subject, $body) { global $notification, $registry; $list = str_replace(self::TASKLIST_EDIT, '', $mbox); /* Create a new iCalendar. */ $vCal = new Horde_Icalendar(); $vCal->setAttribute('PRODID', '-//The Horde Project//IMP ' . $registry->getVersion() . '//EN'); $vCal->setAttribute('METHOD', 'PUBLISH'); /* Create a new vTodo object using this message's contents. */ $vTodo = Horde_Icalendar::newComponent('vtodo', $vCal); $vTodo->setAttribute('SUMMARY', $subject); $vTodo->setAttribute('DESCRIPTION', $body); $vTodo->setAttribute('PRIORITY', '3'); /* Get the list of editable tasklists. */ $lists = $this->getTasklists(true); /* Attempt to add the new vTodo item to the requested tasklist. */ try { $res = $registry->call('tasks/import', array($vTodo, 'text/calendar', $list)); } catch (Horde_Exception $e) { $notification->push($e); return; } if (!$res) { $notification->push(_("An unknown error occured while creating the new task."), 'horde.error'); } elseif (!empty($lists)) { $name = '"' . htmlspecialchars($subject) . '"'; /* Attempt to convert the object name into a hyperlink. */ if ($registry->hasLink('tasks/show')) { $name = sprintf('<a href="%s">%s</a>', Horde::url($registry->link('tasks/show', array('uid' => $res))), $name); } $notification->push(sprintf(_("%s was successfully added to \"%s\"."), $name, htmlspecialchars($lists[$list]->get('name'))), 'horde.success', array('content.raw')); } }
/** * Yet another problem: Outlook seems to remove the organizer from * the iCal when forwarding -- we put the original sender back in * as organizer. * * @param string $icaltext The ical message. * @param MIME_Headers $from The message sender. */ function _addOrganizer(&$icaltxt, $from) { global $conf; if (isset($conf['kolab']['filter']['email_domain'])) { $email_domain = $conf['kolab']['filter']['email_domain']; } else { $email_domain = 'localhost'; } $iCal = new Horde_Icalendar(); $iCal->parsevCalendar($icaltxt); $vevent =& $iCal->findComponent('VEVENT'); if ($vevent) { $organizer = $vevent->getAttribute('ORGANIZER', true); if (is_a($organizer, 'PEAR_Error')) { $adrs = imap_rfc822_parse_adrlist($from, $email_domain); if (count($adrs) > 0) { $org_email = 'mailto:' . $adrs[0]->mailbox . '@' . $adrs[0]->host; $org_name = $adrs[0]->personal; if ($org_name) { $vevent->setAttribute('ORGANIZER', $org_email, array('CN' => $org_name), false); } else { $vevent->setAttribute('ORGANIZER', $org_email, array(), false); } Horde::log(sprintf("Adding missing organizer '%s <%s>' to iCal.", $org_name, $org_email), 'DEBUG'); $icaltxt = $iCal->exportvCalendar(); } } } }
/** * Test creating a Horde_ActiveSync_Message_MeetingRequest from a MIME Email */ public function testInvite() { $this->markTestIncomplete('Has issues on 32bit systems'); $fixture = file_get_contents(__DIR__ . '/fixtures/invitation_one.eml'); $mime = Horde_Mime_Part::parseMessage($fixture); $msg = new Horde_ActiveSync_Message_MeetingRequest(); foreach ($mime->contentTypeMap() as $id => $type) { if ($type == 'text/calendar') { $vcal = new Horde_Icalendar(); $vcal->parseVcalendar($mime->getPart($id)->getContents()); $msg->fromvEvent($vcal); break; } } $stream = fopen('php://memory', 'wb+'); $encoder = new Horde_ActiveSync_Wbxml_Encoder($stream); $msg->encodeStream($encoder); rewind($stream); $results = stream_get_contents($stream); fclose($stream); $stream = fopen(__DIR__ . '/fixtures/meeting_request_one.wbxml', 'r+'); $expected = ''; // Using file_get_contents or even fread mangles the binary data for some // reason. while ($line = fgets($stream)) { $expected .= $line; } fclose($stream); $this->assertEquals($expected, $results); }
private function _getFixture($element) { $iCal = new Horde_Icalendar(); $iCal->parsevCalendar(file_get_contents(__DIR__ . '/../fixtures/allday.ics')); $components = $iCal->getComponents(); return $components[$element]; }
/** * Variables required in form input: * - imple_submit: vcard action. Contains import and source properties * - mime_id * - muid * * @return boolean True on success. */ protected function _handle(Horde_Variables $vars) { global $registry, $injector, $notification; $iCal = new Horde_Icalendar(); try { $contents = $injector->getInstance('IMP_Factory_Contents')->create(new IMP_Indices_Mailbox($vars)); if (!($mime_part = $contents->getMimePart($vars->mime_id))) { throw new IMP_Exception(_("Cannot retrieve vCard data from message.")); } elseif (!$iCal->parsevCalendar($mime_part->getContents(), 'VCALENDAR', $mime_part->getCharset())) { throw new IMP_Exception(_("Error reading the contact data.")); } $components = $iCal->getComponents(); } catch (Exception $e) { $notification->push($e, 'horde.error'); } $import = !empty($vars->imple_submit->import) ? $vars->imple_submit->import : false; $source = !empty($vars->imple_submit->source) ? $vars->imple_submit->source : false; if ($import && $source && $registry->hasMethod('contacts/import')) { $count = 0; foreach ($components as $c) { if ($c->getType() == 'vcard') { try { $registry->call('contacts/import', array($c, null, $source)); ++$count; } catch (Horde_Exception $e) { $notification->push(Horde_Core_Translation::t("There was an error importing the contact data:") . ' ' . $e->getMessage(), 'horde.error'); } } } $notification->push(sprintf(Horde_Core_Translation::ngettext("%d contact was successfully added to your address book.", "%d contacts were successfully added to your address book.", $count), $count), 'horde.success'); } }
/** * Return the rendered inline version of the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInline() { $GLOBALS['page_output']->growler = true; $data = $this->_mimepart->getContents(); $mime_id = $this->_mimepart->getMimeId(); // Parse the iCal file. $vCal = new Horde_Icalendar(); if (!$vCal->parsevCalendar($data, 'VCALENDAR', $this->_mimepart->getCharset())) { $status = new IMP_Mime_Status(_("The calendar data is invalid")); $status->action(IMP_Mime_Status::ERROR); return array($mime_id => array('data' => '', 'status' => $status, 'type' => 'text/html; charset=UTF-8')); } // Check if we got vcard data with the wrong vcalendar mime type. $imp_contents = $this->getConfigParam('imp_contents'); $c = $vCal->getComponentClasses(); if (count($c) == 1 && !empty($c['horde_icalendar_vcard'])) { return $imp_contents->renderMIMEPart($mime_id, IMP_Contents::RENDER_INLINE, array('type' => 'text/x-vcard')); } $imple = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Imple')->create('IMP_Ajax_Imple_ItipRequest', array('mime_id' => $mime_id, 'muid' => strval($imp_contents->getIndicesOb()))); // Get the method type. try { $method = $vCal->getAttribute('METHOD'); } catch (Horde_Icalendar_Exception $e) { $method = ''; } $out = array(); $components = $vCal->getComponents(); foreach ($components as $key => $component) { switch ($component->getType()) { case 'vEvent': try { if ($component->getAttribute('RECURRENCE-ID')) { break; } } catch (Horde_ICalendar_Exception $e) { } $out[] = $this->_vEvent($component, $key, $method, $components); break; case 'vTodo': $out[] = $this->_vTodo($component, $key, $method); break; case 'vTimeZone': // Ignore them. break; case 'vFreebusy': $out[] = $this->_vFreebusy($component, $key, $method); break; // @todo: handle stray vcards here as well. // @todo: handle stray vcards here as well. default: $out[] = sprintf(_("Unhandled component of type: %s"), $component->getType()); break; } } $view = $this->_getViewOb(); $view->formid = $imple->getDomId(); $view->out = implode('', $out); return array($mime_id => array('data' => $view->render('base'), 'type' => 'text/html; charset=UTF-8')); }
public function testGeo() { $ical = new Horde_Icalendar(); $ical->parsevCalendar(file_get_contents(__DIR__ . '/fixtures/geo1.vcf')); $this->assertEquals(array('latitude' => -17.87, 'longitude' => 37.24), $ical->getComponent(0)->getAttribute('GEO')); $ical->parsevCalendar(file_get_contents(__DIR__ . '/fixtures/geo2.vcf')); $this->assertEquals(array('latitude' => 37.386013, 'longitude' => -122.082932), $ical->getComponent(0)->getAttribute('GEO')); }
public function testIgnoringMultipleAttributeValues() { $ical = new Horde_Icalendar(); $ical->parsevCalendar(file_get_contents(__DIR__ . '/fixtures/multiple-summary.ics')); $result = $ical->getComponent(0)->getAttributeSingle('SUMMARY'); $this->assertInternalType('string', $result); $this->assertEquals('Summary 1', $result); }
public function testFiles() { $test_files = glob(__DIR__ . '/fixtures/charset*.ics'); foreach ($test_files as $file) { $ical = new Horde_Icalendar(); $ical->parsevCalendar(file_get_contents($file)); $this->assertEquals('möchen', $ical->getComponent(0)->getAttribute('SUMMARY')); } }
private function _getFixture($name, $item = 0) { $iCal = new Horde_Icalendar(); $iCal->parsevCalendar(file_get_contents(__DIR__ . '/../fixtures/' . $name)); $components = $iCal->getComponents(); $event = new Kronolith_Event_Sql(new Kronolith_Stub_Driver()); $event->fromiCalendar($components[$item]); return $event; }
public function testBug14132() { $ical = new Horde_Icalendar(); $ical->parsevCalendar(file_get_contents(__DIR__ . '/fixtures/bug14132.ics')); $params = $ical->getComponent(1)->getAttribute('DTSTART', true); $tz = $params[0]['TZID']; $start = $ical->getComponent(1)->getAttribute('DTSTART'); $dtstart = new Horde_Date($start, $tz); $this->assertEquals((string) $dtstart, '2015-10-09 03:00:00'); }
/** * Return the response as an iCalendar vEvent object. * * @param Horde_Itip_Response_Type $type The response type. * @param Horde_Icalendar|boolean $vCal The parent container or false if not * provided. * * @return Horde_Icalendar_Vevent The response object. */ public function getVevent(Horde_Itip_Response_Type $type, $vCal = false) { $itip_reply = new Horde_Itip_Event_Vevent(Horde_Icalendar::newComponent('VEVENT', $vCal)); $this->_request->copyEventInto($itip_reply); $type->setRequest($this->_request); $itip_reply->setAttendee($this->_resource->getMailAddress(), $this->_resource->getCommonName(), $type->getStatus()); return $itip_reply->getVevent(); }
/** * @dataProvider timezones */ public function testFile($file) { $result = ''; $ical = new Horde_Icalendar(); $ical->parsevCalendar(file_get_contents($file)); foreach ($ical->getComponents() as $component) { if ($component->getType() != 'vEvent') { continue; } $date = $component->getAttribute('DTSTART'); if (is_array($date)) { continue; } $result .= str_replace("\r", '', $component->getAttribute('SUMMARY')) . "\n"; $d = new Horde_Date($date); $result .= $d->format('H:i') . "\n"; } $this->assertStringEqualsFile(__DIR__ . '/fixtures/vTimezone/' . basename($file, 'ics') . 'txt', $result, 'Failed parsing file ' . basename($file)); }
/** * Process the iCalendar data. * * @return array A hash of UID => id. * @throws Kronolith_Exception */ protected function _process() { $ids = array(); $components = $this->_iCal->getComponents(); if (count($components) == 0) { throw new Kronolith_Exception(_("No iCalendar data was found.")); } foreach ($components as $component) { if (!$this->_preSave($component)) { continue; } try { // RECURRENCE-ID - must import after base event is // imported/saved so defer these until all other data is // processed. $component->getAttribute('RECURRENCE-ID'); $this->_exceptions[] = $component; } catch (Horde_Icalendar_Exception $e) { $event = $this->_driver->getEvent(); $event->fromiCalendar($component, true); // Delete existing exception events. There is no efficient way // to determine if any existing events have been changed/deleted // so we just remove them all since they will be re-added during // the import process. foreach ($event->boundExceptions() as $exception) { $this->_driver->deleteEvent($exception->id); } // Save and post-process. $event->save(); $this->_postSave($event); $ids[$event->uid] = $event->id; } } // Save exception events. foreach ($this->_exceptions as $exception) { $event = $this->_driver->getEvent(); $event->fromiCalendar($exception); $event->save(); } return $ids; }
public function testRead() { $ical = new Horde_Icalendar(); $ical->parsevCalendar(file_get_contents(__DIR__ . '/fixtures/vfreebusy1.ics')); // Get the vFreeBusy component $vfb = $ical->getComponent(0); // Dump the type $this->assertEquals('vFreebusy', $vfb->getType()); // Dump the vfreebusy component again (the duration should be // converted to start/end $this->assertStringEqualsFile(__DIR__ . '/fixtures/vfreebusy2.ics', $vfb->exportvCalendar()); // Dump organizer name $this->assertEquals('GunnarWrobel', $vfb->getName()); // Dump organizer mail $this->assertEquals('*****@*****.**', $vfb->getEmail()); // Dump busy periods $this->assertEquals(array(1164258000 => 1164261600, 1164268800 => 1164276000), $vfb->getBusyPeriods()); // Decode the summary information $extra = $vfb->getExtraParams(); $this->assertEquals('testtermin', base64_decode($extra[1164258000]['X-SUMMARY'])); // Dump the free periods in between the two given time stamps $this->assertEquals(array(1164261600 => 1164268800), $vfb->getFreePeriods(1164261500, 1164268900)); // Dump start of the free/busy information $this->assertEquals(1164236400, $vfb->getStart()); // Dump end of the free/busy information $this->assertEquals(1169420400, $vfb->getEnd()); // Free periods don't get added $vfb->addBusyPeriod('FREE', 1164261600, 1164268800); $this->assertEquals(array(1164258000 => 1164261600, 1164268800 => 1164276000), $vfb->getBusyPeriods()); // Add a busy period with start/end (11:00 / 12:00) $vfb->addBusyPeriod('BUSY', 1164279600, 1164283200); // Add a busy period with start/duration (14:00 / 2h) $vfb->addBusyPeriod('BUSY', 1164290400, null, 7200, array('X-SUMMARY' => 'dGVzdA==')); // Dump busy periods $this->assertEquals(array(1164258000 => 1164261600, 1164268800 => 1164276000, 1164279600 => 1164283200, 1164290400 => 1164297600), $vfb->getBusyPeriods()); // Dump the extra parameters $this->assertEquals(array(1164258000 => array('X-UID' => 'MmZlNWU3NDRmMGFjNjZkNjRjZjFkZmFmYTE4NGFiZTQ=', 'X-SUMMARY' => 'dGVzdHRlcm1pbg=='), 1164268800 => array(), 1164279600 => array(), 1164290400 => array('X-SUMMARY' => 'dGVzdA==')), $vfb->getExtraParams()); return $vfb; }
/** * Retrieve Free/Busy data for the specified resource. * * @param string $resource Fetch the Free/Busy data for this resource. * * @return Horde_Icalendar_Vfreebusy The Free/Busy data. */ public function get($resource) { global $conf; $url = self::getUrl($resource); Horde::log(sprintf('Freebusy URL for resource %s is %s', $resource, $url), 'DEBUG'); list($user, $domain) = explode('@', $resource); if (empty($domain)) { $domain = $conf['kolab']['filter']['email_domain']; } /** * This section matches Kronolith_Freebusy and should be merged with it * again in a single Horde_Freebusy module. */ $options = array('method' => 'GET', 'timeout' => 5, 'allowRedirects' => true); if (!empty($conf['http']['proxy']['proxy_host'])) { $options = array_merge($options, $conf['http']['proxy']); } $http = new HTTP_Request($url, $options); $http->setBasicAuth($conf['kolab']['filter']['calendar_id'] . '@' . $domain, $conf['kolab']['filter']['calendar_pass']); @$http->sendRequest(); if ($http->getResponseCode() != 200) { throw new Horde_Kolab_Resource_Exception(sprintf('Unable to retrieve free/busy information for %s', $resource), Horde_Kolab_Resource_Exception::NO_FREEBUSY); } $vfb_text = $http->getResponseBody(); // Detect the charset of the iCalendar data. $contentType = $http->getResponseHeader('Content-Type'); if ($contentType && strpos($contentType, ';') !== false) { list(, $charset, ) = explode(';', $contentType); $vfb_text = Horde_String::convertCharset($vfb_text, trim(str_replace('charset=', '', $charset)), 'UTF-8'); } $iCal = new Horde_Icalendar(); $iCal->parsevCalendar($vfb_text, 'VCALENDAR'); $vfb =& $iCal->findComponent('VFREEBUSY'); if ($vfb === false) { throw new Horde_Kolab_Resource_Exception(sprintf('Invalid or no free/busy information available for %s', $resource), Horde_Kolab_Resource_Exception::NO_FREEBUSY); } $vfb->simplify(); return $vfb; }
/** * @param mixed Kronolith_Event|string $event The event object or error * string to display. */ public function __construct($event) { if (!$event) { echo '<h3>' . _("Event not found") . '</h3>'; exit; } if (is_string($event)) { echo '<h3>' . $event . '</h3>'; exit; } $iCal = new Horde_Icalendar('2.0'); if ($event->calendarType == 'internal') { try { $share = $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($event->calendar); $iCal->setAttribute('X-WR-CALNAME', $share->get('name')); } catch (Exception $e) { } } $iCal->addComponent($event->toiCalendar($iCal)); $content = $iCal->exportvCalendar(); $GLOBALS['browser']->downloadHeaders($event->getTitle() . '.ics', 'text/calendar; charset=UTF-8', true, strlen($content)); echo $content; exit; }
/** * Return the full rendered version of the Horde_Mime_Part object. * * URL parameters used by this function: * - c: (integer) The VCARD component that contains an image. * - p: (integer) The index of image inside the component to display. * * @return array See parent::render(). * @throws Horde_Exception */ protected function _renderInline() { $vars = $GLOBALS['injector']->getInstance('Horde_Variables'); if (!isset($vars->p)) { $imp_contents = $this->getConfigParam('imp_contents'); $GLOBALS['injector']->getInstance('Horde_Core_Factory_Imple')->create('IMP_Ajax_Imple_VcardImport', array('mime_id' => $this->_mimepart->getMimeId(), 'muid' => strval($imp_contents->getIndicesOb()))); $this->_imageUrl = $this->getConfigParam('imp_contents')->urlView($this->_mimepart, 'download_render', array('params' => array('mode' => IMP_Contents::RENDER_INLINE))); return parent::_renderInline(); } /* Send the requested photo. */ $data = $this->_mimepart->getContents(); $ical = new Horde_Icalendar(); if (!$ical->parsevCalendar($data, 'VCALENDAR', $this->_mimepart->getCharset())) { // TODO: Error reporting return array(); } $components = $ical->getComponents(); if (!isset($components[$vars->c])) { // TODO: Error reporting return array(); } $name = $components[$vars->c]->getAttributeDefault('FN', false); if ($name === false) { $name = $components[$vars->c]->printableName(); } if (empty($name)) { $name = preg_replace('/\\..*?$/', '', $this->_mimepart->getName()); } $photos = $components[$vars->c]->getAllAttributes('PHOTO'); if (!isset($photos[$vars->p])) { // TODO: Error reporting return array(); } $type = 'image/' . Horde_String::lower($photos[$vars->p]['params']['TYPE']); return array($this->_mimepart->getMimeId() => array('data' => base64_decode($photos[$vars->p]['value']), 'name' => $name . '.' . Horde_Mime_Magic::mimeToExt($type), 'type' => $type)); }
/** * @throws Kronolith_Exception */ public function search($email, $private_only = false) { $server = $GLOBALS['injector']->getInstance('Horde_Kolab_Session')->getFreebusyServer(); if (empty($server)) { throw new Horde_Exception_NotFound(); } $http = $GLOBALS['injector']->getInstance('Horde_Core_Factory_HttpClient')->create(array('request.username' => $GLOBALS['registry']->getAuth(), 'request.password' => $GLOBALS['registry']->getAuthCredential('password'))); try { $response = $http->get(sprintf('%s/%s.xfb', $server, $email)); } catch (Horde_Http_Exception $e) { throw new Horde_Exception_NotFound(); } if ($response->code != 200) { throw new Horde_Exception_NotFound(); } $vfb_text = $response->getBody(); $iCal = new Horde_Icalendar(); $iCal->parsevCalendar($vfb_text); $vfb = $iCal->findComponent('VFREEBUSY'); if ($vfb === false) { throw new Horde_Exception_NotFound(); } return $vfb; }
/** * Groks the TZID and returns an offset in seconds from UTC for this * date and time. * * @param array $date A date hash. * @param array $time A time hash. * @param string $tzid A timezone ID. * * @return integer The offset from UTC in seconds for the provided * timezone and date/time. */ protected function _parseTZID($date, $time, $tzid) { $vtimezone = $this->_container->findComponentByAttribute('vtimezone', 'TZID', $tzid); if (!$vtimezone) { return false; } $change_times = array(); foreach ($vtimezone->getComponents() as $o) { $change_times = array_merge($change_times, $vtimezone->parseChild($o, $date['year'])); } if (!$change_times) { return false; } usort($change_times, function ($a, $b) { if (!$a['end']) { if (!$b['end']) { return $a['time'] - $b['time']; } return 1; } if (!$b['end']) { return -1; } return $a['end'] - $b['end']; }); // Time is arbitrarily based on UTC for comparison. $t = @gmmktime($time['hour'], $time['minute'], $time['second'], $date['month'], $date['mday'], $date['year']); if ($t < $change_times[0]['time']) { return $change_times[0]['from']; } for ($i = 0, $n = count($change_times); $i < $n - 1; $i++) { // See Bug: 14153. Some timezone definitions may be such that a // transition will incorrectly match due to the way we parse the // 'end' times. There *may* be a more correct way to do this by // sorting the transitions/handling 'end' values differently. if ($t >= $change_times[$i]['time'] && $t < $change_times[$i + 1]['time'] && $this->_checkEndDate($t, $change_times[$i + 1])) { return $change_times[$i]['to']; } } if ($t >= $change_times[$n - 1]['time']) { return $change_times[$n - 1]['to']; } return false; }
/** * Parses a string containing vFreebusy data. * * @param string $data The data to parse. * @param $type TODO * @param $charset TODO */ public function parsevCalendar($data, $type = null, $charset = null) { parent::parsevCalendar($data, 'VFREEBUSY', $charset); // Do something with all the busy periods. foreach ($this->_attributes as $key => $attribute) { if ($attribute['name'] != 'FREEBUSY') { continue; } foreach ($attribute['values'] as $value) { $params = isset($attribute['params']) ? $attribute['params'] : array(); if (isset($value['duration'])) { $this->addBusyPeriod('BUSY', $value['start'], null, $value['duration'], $params); } else { $this->addBusyPeriod('BUSY', $value['start'], $value['end'], null, $params); } } unset($this->_attributes[$key]); } }
/** * Groks the TZID and returns an offset in seconds from UTC for this * date and time. * * @param array $date A date hash. * @param array $time A time hash. * @param string $tzid A timezone ID. * * @return integer The offset from UTC in seconds for the provided * timezone and date/time. */ protected function _parseTZID($date, $time, $tzid) { $vtimezone = $this->_container->findComponentByAttribute('vtimezone', 'TZID', $tzid); if (!$vtimezone) { return false; } $change_times = array(); foreach ($vtimezone->getComponents() as $o) { $change_times = array_merge($change_times, $vtimezone->parseChild($o, $date['year'])); } if (!$change_times) { return false; } usort($change_times, function ($a, $b) { if (!$a['end']) { if (!$b['end']) { return $a['time'] - $b['time']; } return 1; } if (!$b['end']) { return -1; } return $a['end'] - $b['end']; }); // Time is arbitrarily based on UTC for comparison. $t = @gmmktime($time['hour'], $time['minute'], $time['second'], $date['month'], $date['mday'], $date['year']); if ($t < $change_times[0]['time']) { return $change_times[0]['from']; } for ($i = 0, $n = count($change_times); $i < $n - 1; $i++) { if ($t >= $change_times[$i]['time'] && $t < $change_times[$i + 1]['time']) { return $change_times[$i]['to']; } } if ($t >= $change_times[$n - 1]['time']) { return $change_times[$n - 1]['to']; } return false; }
/** * Variables required in form input: * - identity (TODO: ? Code uses it, but it is never set anywhere) * - imple_submit: itip_action(s) * - mime_id * - muid * * @return boolean True on success. */ protected function _handle(Horde_Variables $vars) { global $injector, $notification, $registry; $actions = (array) $vars->imple_submit; $result = false; $vCal = new Horde_Icalendar(); /* Retrieve the calendar data from the message. */ try { $contents = $injector->getInstance('IMP_Factory_Contents')->create(new IMP_Indices_Mailbox($vars)); $mime_part = $contents->getMIMEPart($vars->mime_id); if (empty($mime_part)) { throw new IMP_Exception(_("Cannot retrieve calendar data from message.")); } elseif (!$vCal->parsevCalendar($mime_part->getContents(), 'VCALENDAR', $mime_part->getCharset())) { throw new IMP_Exception(_("The calendar data is invalid")); } $components = $vCal->getComponents(); } catch (Exception $e) { $notification->push($e, 'horde.error'); $actions = array(); } foreach ($actions as $key => $action) { $pos = strpos($key, '['); $key = substr($key, $pos + 1, strlen($key) - $pos - 2); switch ($action) { case 'delete': // vEvent cancellation. if ($registry->hasMethod('calendar/delete')) { $guid = $components[$key]->getAttribute('UID'); $recurrenceId = null; try { // This is a cancellation of a recurring event instance. $recurrenceId = $components[$key]->getAttribute('RECURRENCE-ID'); $atts = $components[$key]->getAttribute('RECURRENCE-ID', true); $range = null; foreach ($atts as $att) { if (array_key_exists('RANGE', $att)) { $range = $att['RANGE']; } } } catch (Horde_Icalendar_Exception $e) { } try { $registry->call('calendar/delete', array($guid, $recurrenceId, $range)); $notification->push(_("Event successfully deleted."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error deleting the event: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'update': // vEvent reply. if ($registry->hasMethod('calendar/updateAttendee')) { try { $from = $contents->getHeader()->getOb('from'); $registry->call('calendar/updateAttendee', array($components[$key], $from[0]->bare_address)); $notification->push(_("Respondent Status Updated."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error updating the event: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'import': case 'accept-import': // vFreebusy reply. // vFreebusy publish. // vEvent request. // vEvent publish. // vTodo publish. // vJournal publish. switch ($components[$key]->getType()) { case 'vEvent': $result = $this->_handlevEvent($key, $components, $mime_part); // Must check for exceptions. foreach ($components as $k => $component) { try { if ($component->getType() == 'vEvent' && $component->getAttribute('RECURRENCE-ID')) { $uid = $component->getAttribute('UID'); if ($uid == $components[$key]->getAttribute('UID')) { $this->_handlevEvent($k, $components, $mime_part); } } } catch (Horde_Icalendar_Exception $e) { } } break; case 'vFreebusy': // Import into Kronolith. if ($registry->hasMethod('calendar/import_vfreebusy')) { try { $registry->call('calendar/import_vfreebusy', array($components[$key])); $notification->push(_("The user's free/busy information was sucessfully stored."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error importing user's free/busy information: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'vTodo': // Import into Nag. if ($registry->hasMethod('tasks/import')) { try { $guid = $registry->call('tasks/import', array($components[$key], $mime_part->getType())); $url = Horde::url($registry->link('tasks/show', array('uid' => $guid))); $notification->push(_("The task has been added to your tasklist.") . ' ' . Horde::link($url, _("View task"), null, '_blank') . Horde_Themes_Image::tag('mime/icalendar.png', array('alt' => _("View task"))) . '</a>', 'horde.success', array('content.raw')); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error importing the task: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'vJournal': default: $notification->push(_("This action is not supported."), 'horde.warning'); } if ($action == 'import') { break; } // Fall-through for 'accept-import' // Fall-through for 'accept-import' case 'accept': case 'deny': case 'tentative': // vEvent request. if (isset($components[$key]) && $components[$key]->getType() == 'vEvent') { $vEvent = $components[$key]; $resource = new Horde_Itip_Resource_Identity($injector->getInstance('IMP_Identity'), $vEvent->getAttribute('ATTENDEE'), $vars->identity); switch ($action) { case 'accept': case 'accept-import': $type = new Horde_Itip_Response_Type_Accept($resource); break; case 'deny': $type = new Horde_Itip_Response_Type_Decline($resource); break; case 'tentative': $type = new Horde_Itip_Response_Type_Tentative($resource); break; } try { // Send the reply. Horde_Itip::factory($vEvent, $resource)->sendMultiPartResponse($type, new Horde_Core_Itip_Response_Options_Horde('UTF-8', array()), $injector->getInstance('IMP_Mail')); $notification->push(_("Reply Sent."), 'horde.success'); $result = true; } catch (Horde_Itip_Exception $e) { $notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'send': case 'reply': case 'reply2m': // vfreebusy request. if (isset($components[$key]) && $components[$key]->getType() == 'vFreebusy') { $vFb = $components[$key]; // Get the organizer details. try { $organizer = parse_url($vFb->getAttribute('ORGANIZER')); } catch (Horde_Icalendar_Exception $e) { break; } $organizerEmail = $organizer['path']; $organizer = $vFb->getAttribute('ORGANIZER', true); $organizerFullEmail = new Horde_Mail_Rfc822_Address($organizerEmail); if (isset($organizer['cn'])) { $organizerFullEmail->personal = $organizer['cn']; } if ($action == 'reply2m') { $startStamp = time(); $endStamp = $startStamp + 60 * 24 * 3600; } else { try { $startStamp = $vFb->getAttribute('DTSTART'); } catch (Horde_Icalendar_Exception $e) { $startStamp = time(); } try { $endStamp = $vFb->getAttribute('DTEND'); } catch (Horde_Icalendar_Exception $e) { } if (!$endStamp) { try { $duration = $vFb->getAttribute('DURATION'); $endStamp = $startStamp + $duration; } catch (Horde_Icalendar_Exception $e) { $endStamp = $startStamp + 60 * 24 * 3600; } } } $vfb_reply = $registry->call('calendar/getFreeBusy', array($startStamp, $endStamp)); // Find out who we are and update status. $identity = $injector->getInstance('IMP_Identity'); $email = $identity->getFromAddress(); // Build the reply. $msg_headers = new Horde_Mime_Headers(); $vCal = new Horde_Icalendar(); $vCal->setAttribute('PRODID', '-//The Horde Project//' . $msg_headers->getUserAgent() . '//EN'); $vCal->setAttribute('METHOD', 'REPLY'); $vCal->addComponent($vfb_reply); $message = _("Attached is a reply to a calendar request you sent."); $body = new Horde_Mime_Part(); $body->setType('text/plain'); $body->setCharset('UTF-8'); $body->setContents(Horde_String::wrap($message, 76)); $ics = new Horde_Mime_Part(); $ics->setType('text/calendar'); $ics->setCharset('UTF-8'); $ics->setContents($vCal->exportvCalendar()); $ics->setName('icalendar.ics'); $ics->setContentTypeParameter('METHOD', 'REPLY'); $mime = new Horde_Mime_Part(); $mime->addPart($body); $mime->addPart($ics); // Build the reply headers. $msg_headers->addReceivedHeader(array('dns' => $injector->getInstance('Net_DNS2_Resolver'), 'server' => $conf['server']['name'])); $msg_headers->addMessageIdHeader(); $msg_headers->addHeader('Date', date('r')); $msg_headers->addHeader('From', $email); $msg_headers->addHeader('To', $organizerFullEmail); $identity->setDefault($vars->identity); $replyto = $identity->getValue('replyto_addr'); if (!empty($replyto) && !$email->match($replyto)) { $msg_headers->addHeader('Reply-To', $replyto); } $msg_headers->addHeader('Subject', _("Free/Busy Request Response")); // Send the reply. try { $mime->send($organizerEmail, $msg_headers, $injector->getInstance('IMP_Mail')); $notification->push(_("Reply Sent."), 'horde.success'); $result = true; } catch (Exception $e) { $notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("Invalid Action selected for this component."), 'horde.warning'); } break; case 'nosup': // vFreebusy request. // vFreebusy request. default: $notification->push(_("This action is not supported."), 'horde.warning'); break; } } return $result; }
/** * Return the attendee participation status. * * @param Horde_Icalendar $vCal The vCalendar component. * * @param Horde_Icalendar * @throws Horde_ActiveSync_Exception */ protected function _getiTipStatus($vCal) { foreach ($vCal->getComponents() as $component) { switch ($component->getType()) { case 'vEvent': try { $atparams = $component->getAttribute('ATTENDEE', true); } catch (Horde_Icalendar_Exception $e) { throw new Horde_ActiveSync_Exception($e); } if (!is_array($atparams)) { throw new Horde_Icalendar_Exception('Unexpected value'); } return $atparams[0]['PARTSTAT']; } } }
public function vtodo2sif($vcard) { $iCal = new Horde_Icalendar(); if (!$iCal->parsevCalendar($vcard)) { return PEAR::raiseError('There was an error importing the data.'); } $components = $iCal->getComponents(); switch (count($components)) { case 0: return PEAR::raiseError('No data was found'); case 1: $content = $components[0]; break; default: return PEAR::raiseError('Multiple components found; only one is supported.'); } $hash['Complete'] = 0; $due = false; $attr = $content->getAllAttributes(); foreach ($attr as $item) { switch ($item['name']) { case 'SUMMARY': $hash['Subject'] = $item['value']; break; case 'DESCRIPTION': $hash['Body'] = $item['value']; break; case 'PRIORITY': if ($item['value'] == 1) { $hash['Importance'] = 2; } elseif ($item['value'] == 5) { $hash['Importance'] = 0; } else { $hash['Importance'] = 1; } break; case 'DTSTART': $hash['StartDate'] = Horde_Icalendar::_exportDateTime($item['value']); break; case 'DUE': $hash['DueDate'] = Horde_Icalendar::_exportDateTime($item['value']); $due = $item['value']; break; case 'AALARM': $hash['ReminderTime'] = $item['value']; $hash['ReminderSet'] = 1; break; case 'STATUS': $hash['Complete'] = $item['value'] == 'COMPLETED' ? 1 : 0; break; case 'CATEGORIES': $hash['Categories'] = $item['value']; break; case 'CLASS': switch (Horde_String::upper($item['value'])) { case 'PUBLIC': $hash['Sensitivity'] = 0; break; case 'PRIVATE': $hash['Sensitivity'] = 2; break; case 'CONFIDENTIAL': $hash['Sensitivity'] = 3; break; } break; } } if ($due && !isset($hash['ReminderSet'])) { // Parse VALARM components. foreach ($content->getComponents() as $component) { if ($component->getType() != 'vAlarm') { continue; } try { $trigger = $component->getAttribute('TRIGGER'); } catch (Horde_Icalendar_Exception $e) { continue; } if (is_array($trigger) || empty($trigger)) { continue; } $hash['ReminderSet'] = 1; $hash['ReminderTime'] = Horde_Icalendar::_exportDateTime($due - $trigger); } } return Horde_SyncMl_Device_sync4j::array2sif($hash, '<?xml version="1.0"?><task>', '</task>'); }
$notification->push(_("Calendar successfully purged."), 'horde.success'); } catch (Exception $e) { $notification->push(sprintf(_("The calendar could not be purged: %s"), $e->getMessage()), 'horde.error'); } } } $recurrences = array(); $ical = null; foreach ($next_step as $row) { if ($max_events !== true && $num_events >= $max_events) { Horde::permissionDeniedError('kronolith', 'max_events', sprintf(_("You are not allowed to create more than %d events."), $perms->hasAppPermission('max_events'))); break; } if ($row instanceof Horde_Icalendar_Vevent) { if (!$ical) { $ical = new Horde_Icalendar(); } $ical->addComponent($row); if ($max_events !== true) { $num_events++; } continue; } try { $event = $kronolith_driver->getEvent(); } catch (Exception $e) { $msg = _("Can't create a new event.") . ' ' . sprintf(_("This is what the server said: %s"), $e->getMessage()); $notification->push($msg, 'horde.error'); $error = true; break; }
/** * Handle parsing recurrence related fields. * * @param Horde_Icalendar $vEvent * @throws Kronolith_Exception */ protected function _handlevEventRecurrence($vEvent) { // Recurrence. try { $rrule = $vEvent->getAttribute('RRULE'); if (!is_array($rrule)) { $this->recurrence = new Horde_Date_Recurrence($this->start); if (strpos($rrule, '=') !== false) { $this->recurrence->fromRRule20($rrule); } else { $this->recurrence->fromRRule10($rrule); } /* Delete all existing exceptions to this event if it * already exists */ if (!empty($this->uid)) { $kronolith_driver = Kronolith::getDriver(null, $this->calendar); $search = new StdClass(); $search->start = $this->recurrence->getRecurStart(); $search->end = $this->recurrence->getRecurEnd(); $search->baseid = $this->uid; $results = $kronolith_driver->search($search); foreach ($results as $days) { foreach ($days as $exception) { $kronolith_driver->deleteEvent($exception->id); } } } // Exceptions. EXDATE represents deleted events, just add the // exception, no new event is needed. $exdates = $vEvent->getAttributeValues('EXDATE'); if (is_array($exdates)) { foreach ($exdates as $exdate) { if (is_array($exdate)) { $this->recurrence->addException((int) $exdate['year'], (int) $exdate['month'], (int) $exdate['mday']); } } } } } catch (Horde_Icalendar_Exception $e) { } // RECURRENCE-ID indicates that this event represents an exception try { $recurrenceid = $vEvent->getAttribute('RECURRENCE-ID'); $kronolith_driver = Kronolith::getDriver(null, $this->calendar); $originaldt = new Horde_Date($recurrenceid); $this->exceptionoriginaldate = $originaldt; $this->baseid = $this->uid; $this->uid = null; try { $originalEvent = $kronolith_driver->getByUID($this->baseid); $originalEvent->recurrence->addException($originaldt->format('Y'), $originaldt->format('m'), $originaldt->format('d')); $originalEvent->save(); } catch (Horde_Exception_NotFound $e) { throw new Kronolith_Exception(_("Unable to locate original event series.")); } } catch (Horde_Icalendar_Exception $e) { } }
/** * Export this memo in iCalendar format. * * @param array memo The memo (hash array) to export * @param Horde_Icalendar A Horde_Icalendar object that acts as container. * * @return Horde_Icalendar_Vnote object for this event. */ public function toiCalendar($memo, $calendar) { global $prefs; $vnote = Horde_Icalendar::newComponent('vnote', $calendar); $vnote->setAttribute('UID', $memo['uid']); $vnote->setAttribute('BODY', $memo['body']); $vnote->setAttribute('SUMMARY', $memo['desc']); if (!empty($memo['tags'])) { $vnote->setAttribute('CATEGORIES', implode(', ', $memo['tags'])); } /* Get the note's history. */ $history = $GLOBALS['injector']->getInstance('Horde_History'); $log = $history->getHistory('mnemo:' . $memo['memolist_id'] . ':' . $memo['uid']); if ($log) { foreach ($log as $entry) { switch ($entry['action']) { case 'add': $created = $entry['ts']; break; case 'modify': $modified = $entry['ts']; break; } } } if (!empty($created)) { $vnote->setAttribute('DCREATED', $created); } if (!empty($modified)) { $vnote->setAttribute('LAST-MODIFIED', $modified); } return $vnote; }
protected function toHash($vcard, $map = array()) { $driver = new Turba_Driver(); foreach ($map as $field => $config) { $driver->map[$field] = $config; } $ical = new Horde_Icalendar(); $ical->parsevCalendar($vcard); return $driver->toHash($ical->getComponent(0)); }
/** * Replaces the event identified by UID with the content represented in the * specified contentType. * * @param string $uid Idenfity the event to replace. * @param mixed $content The content of the event. String or * Horde_Icalendar_Vevent * @param string $contentType What format is the data in? Currently supports: * text/calendar * text/x-vcalendar * (Ignored if content is Horde_Icalendar_Vevent) * activesync (Horde_ActiveSync_Message_Appointment) * @param string $calendar Ensure the event is replaced in the specified * calendar. @since 4.2.0 * * @return mixed For EAS operations, an array of 'uid' and 'atchash' * are returned. @since 4.3.0 * @throws Kronolith_Exception */ public function replace($uid, $content, $contentType, $calendar = null) { $event = Kronolith::getDriver(null, $calendar)->getByUID($uid); if (!$event->hasPermission(Horde_Perms::EDIT) || $event->private && $event->creator != $GLOBALS['registry']->getAuth()) { throw new Horde_Exception_PermissionDenied(); } if ($content instanceof Horde_Icalendar_Vevent) { $component = $content; } elseif ($content instanceof Horde_ActiveSync_Message_Appointment) { $event->fromASAppointment($content); $atc_hash = $event->addEASFiles($content); $event->save(); $event->uid = $uid; return array('uid' => $event->uid, 'atchash' => $atc_hash); return; } else { switch ($contentType) { case 'text/calendar': case 'text/x-vcalendar': if (!$content instanceof Horde_Icalendar_Vevent) { $iCal = new Horde_Icalendar(); if (!$iCal->parsevCalendar($content)) { throw new Kronolith_Exception(_("There was an error importing the iCalendar data.")); } $components = $iCal->getComponents(); $component = null; foreach ($components as $content) { if ($content instanceof Horde_Icalendar_Vevent) { if ($component !== null) { throw new Kronolith_Exception(_("Multiple iCalendar components found; only one vEvent is supported.")); } $component = $content; } } if ($component === null) { throw new Kronolith_Exception(_("No iCalendar data was found.")); } } break; default: throw new Kronolith_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType)); } } try { $component->getAttribute('RECURRENCE-ID'); $this->_addiCalEvent($component, Kronolith::getDriver(null, $calendar), true); } catch (Horde_Icalendar_Exception $e) { $event->fromiCalendar($component, true); // Ensure we keep the original UID, even when content does not // contain one and fromiCalendar creates a new one. $event->uid = $uid; $event->save(); } }