* @package com_icagenda * @copyright Copyright (c)2012-2015 Cyril Rezé, Jooml!C - All rights reserved * * @license GNU General Public License version 3 or later; see LICENSE.txt * @author Tom-Henning (MaW) / Cyril Rezé (Lyr!C) * @link http://www.joomlic.com * * @version 3.5.6 2015-05-11 * @since 3.2.9 *------------------------------------------------------------------------------ */ // No direct access to this file defined('_JEXEC') or die; require_once JPATH_COMPONENT . '/helpers/iCalcreator.class.php'; //$v = new vCalendar($config); $v = new vCalendar(); $v->setConfig('filename', 'icagenda.ics'); $v->prodid = 'iCagenda'; $config = JFactory::getConfig(); // Joomla 3.x / 2.5 SWITCH if (version_compare(JVERSION, '3.0', 'ge')) { $offset = $config->get('offset'); } else { $offset = $config->getValue('config.offset'); } $dateTimeZone = new DateTimeZone($offset); $dateTime = new DateTime("now", $dateTimeZone); $timeOffset = $dateTimeZone->getOffset($dateTime); $timezone = $timeOffset / 3600; $tz = 'UTC'; $v->setProperty('method', 'PUBLISH');
function handle_remote_attendee_reply(vCalendar $ical) { $attendees = $ical->GetAttendees(); // attendee reply have just one attendee if (count($attendees) != 1) { return; } $attendee = $attendees[0]; $uidparam = $ical->GetPropertiesByPath("VCALENDAR/*/UID"); $uid = $uidparam[0]->Value(); $qry = new AwlQuery('UPDATE calendar_attendee SET email_status=:statusTo WHERE attendee=:attendee AND dav_id = (SELECT dav_id FROM calendar_item WHERE uid = :uid)'); // user accepted $qry->Bind(':statusTo', EMAIL_STATUS::NORMAL); $qry->Bind(':attendee', $attendee->Value()); $qry->Bind(':uid', $uid); $qry->Exec('changeStatusTo'); return true; }
/** * While we can construct our SQL to apply some filters in the query, other filters * need to be checked against the retrieved record. This is for handling those ones. * * @param array $filter An array of XMLElement which is the filter definition * @param string $item The database row retrieved for this calendar item * * @return boolean True if the check succeeded, false otherwise. */ function apply_filter($filters, $item) { global $session, $c, $request; if (count($filters) == 0) { return true; } dbg_error_log("calquery", "Applying filter for item '%s'", $item->dav_name); $ical = new vCalendar($item->caldav_data); return $ical->StartFilter($filters); }
/** * parses a STRING represeting an iCalendar (vCalendar 2) * @param STRING $str - iCalendar formatted String * @return */ function parse($str) { $this->data = array(); $lines = explode("\n", $str); $cur = null; for ($i = 0; $i < count($lines); $i++) { $line = trim($lines[$i]); while (!empty($lines[$i + 1]) && substr($lines[$i + 1], 0, 1) == " ") { $line .= trim(substr($lines[$i + 1], 1)); $i++; } $kv = explode(':', $line, 2); if (count($kv) == 1) { continue; } $key = trim($kv[0]); $value = trim($kv[1]); switch ($key) { case 'END': switch ($value) { case 'VCALENDAR': if ($cur) { $this->data['calendar'][] = $cur; $cur = null; } break; default: if ($cur) { $cur->process($key, $value); } else { throw new Exception('Invalid END Tag - ' . $line); } } break; case 'BEGIN': switch ($value) { case 'VCALENDAR': $cur = new vCalendar(); break; default: if ($cur) { $cur->process($key, $value); } else { throw new Exception('Invalid BEGIN Tag - ' . $line); } } break; default: if ($cur) { $cur->process($key, $value); } else { throw new Exception('Invalid Tag - ' . $line); } } } }
function write_updated_zone($vtimezone, $tzid) { global $new_zones, $modified_zones; if (empty($vtimezone)) { dbg_error_log('tz/updatecheck', 'Skipping zone "%s" - no data from server', $tzid); return; } $tzrow = fetch_db_zone($tzid); if (isset($tzrow) && $vtimezone == $tzrow->vtimezone) { dbg_error_log('tz/updatecheck', 'Skipping zone "%s" - no change', $tzid); return; } $vtz = new vCalendar($vtimezone); $last_modified = $vtz->GetPValue('LAST-MODIFIED'); if (empty($last_modified)) { $last_modified = gmdate('Ymd\\THis\\Z'); // Then it was probably that way when we last updated the data, too :-( if (!empty($tzrow)) { $old_vtz = new vCalendar($tzrow->vtimezone); $old_vtz->ClearProperties('LAST-MODIFIED'); // We need to add & remove this property so the Render is equivalent. $vtz->AddProperty('LAST-MODIFIED', $last_modified); $vtz->ClearProperties('LAST-MODIFIED'); if ($vtz->Render() == $old_vtz->Render()) { dbg_error_log('tz/updatecheck', 'Skipping zone "%s" - no change', $tzid); return; } } $vtz->AddProperty('LAST-MODIFIED', $last_modified); } dbg_error_log('tz/updatecheck', 'Writing %s zone for "%s"', empty($tzrow) ? "new" : "updated", $tzid); printf("Writing %s zone for '%s'\n", empty($tzrow) ? "new" : "updated", $tzid); $params = array(':tzid' => $tzid, ':olson_name' => $tzid, ':vtimezone' => $vtz->Render(), ':last_modified' => $last_modified, ':etag' => md5($vtz->Render())); if (empty($tzrow)) { $new_zones++; $sql = 'INSERT INTO timezones(tzid,active,olson_name,last_modified,etag,vtimezone) '; $sql .= 'VALUES(:tzid,TRUE,:olson_name,:last_modified,:etag,:vtimezone)'; } else { $modified_zones++; $sql = 'UPDATE timezones SET active=TRUE, olson_name=:olson_name, last_modified=:last_modified, '; $sql .= 'etag=:etag, vtimezone=:vtimezone WHERE tzid=:tzid'; } $qry = new AwlQuery($sql, $params); $qry->Exec('tz/update', __LINE__, __FILE__); }
$transparency = $setting->GetPath('urn:ietf:params:xml:ns:caldav:schedule-calendar-transp/*'); $transparency = preg_replace('{^.*:}', '', $transparency[0]->GetNSTag()); $qry->QDo('UPDATE collection SET schedule_transp = :transparency WHERE dav_name = :dav_name', array(':dav_name' => $dav_resource->dav_name(), ':transparency' => $transparency)); $success[$tag] = 1; } else { add_failure('set', $tag, 'HTTP/1.1 409 Conflict', translate("The CalDAV:schedule-calendar-transp property may only be set on calendars.")); } break; case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set': add_failure('set', $tag, 'HTTP/1.1 409 Conflict', translate("The calendar-free-busy-set is superseded by the schedule-calendar-transp property of a calendar collection.")); break; case 'urn:ietf:params:xml:ns:caldav:calendar-timezone': if ($dav_resource->IsCollection() && $dav_resource->IsCalendar() && !$dav_resource->IsBinding()) { $tzcomponent = $setting->GetPath('urn:ietf:params:xml:ns:caldav:calendar-timezone'); $tzstring = $tzcomponent[0]->GetContent(); $calendar = new vCalendar($tzstring); $timezones = $calendar->GetComponents('VTIMEZONE'); if (count($timezones) == 0) { break; } $tz = $timezones[0]; // Backward compatibility $tzid = $tz->GetPValue('TZID'); $params = array(':tzid' => $tzid); $qry = new AwlQuery('SELECT 1 FROM timezones WHERE tzid = :tzid', $params); if ($qry->Exec('PUT', __LINE__, __FILE__) && $qry->rows() == 0) { $params[':olson_name'] = $calendar->GetOlsonName($tz); $params[':vtimezone'] = isset($tz) ? $tz->Render() : null; $qry->QDo('INSERT INTO timezones (tzid, olson_name, active, vtimezone) VALUES(:tzid,:olson_name,false,:vtimezone)', $params); } $qry->QDo('UPDATE collection SET timezone = :tzid WHERE dav_name = :dav_name', array(':tzid' => $tzid, ':dav_name' => $dav_resource->dav_name()));
function handle_freebusy_request($ic) { global $c, $session, $request, $ical; $request->NeedPrivilege('CALDAV:schedule-send-freebusy'); $reply = new XMLDocument(array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C")); $responses = array(); $fbq_start = $ic->GetPValue('DTSTART'); $fbq_end = $ic->GetPValue('DTEND'); if (!(isset($fbq_start) || isset($fbq_end))) { $request->DoResponse(400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND'); } $range_start = new RepeatRuleDateTime($fbq_start); $range_end = new RepeatRuleDateTime($fbq_end); $attendees = $ic->GetProperties('ATTENDEE'); if (preg_match('# iCal/\\d#', $_SERVER['HTTP_USER_AGENT'])) { dbg_error_log("POST", "Non-compliant iCal request. Using X-WR-ATTENDEE property"); $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE'); foreach ($wr_attendees as $k => $v) { $attendees[] = $v; } } dbg_error_log("POST", "Responding with free/busy for %d attendees", count($attendees)); foreach ($attendees as $k => $attendee) { $attendee_email = preg_replace('/^mailto:/', '', $attendee->Value()); dbg_error_log("POST", "Calculating free/busy for %s", $attendee_email); /** @todo Refactor this so we only do one query here and loop through the results */ $params = array(':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email); $qry = new AwlQuery('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params); if (!$qry->Exec('POST', __LINE__, __FILE__)) { $request->DoResponse(501, 'Database error'); } if ($qry->rows() > 1) { // Unlikely, but if we get more than one result we'll do an exact match instead. if (!$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params)) { $request->DoResponse(501, 'Database error'); } if ($qry->rows() == 0) { /** Sigh... Go back to the original case-insensitive match */ $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params); } } $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav'); $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value())); if ($qry->rows() == 0) { $remote = new iSchedule(); $answer = $remote->sendRequest($attendee->Value(), 'VFREEBUSY/REQUEST', $ical->Render()); if ($answer === false) { $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User"); $reply->CalDAVElement($response, "calendar-data"); $responses[] = $response; continue; } foreach ($answer as $a) { if ($a === false) { $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User"); $reply->CalDAVElement($response, "calendar-data"); } elseif (substr($a, 0, 1) >= 1) { $reply->CalDAVElement($response, "request-status", $a); $reply->CalDAVElement($response, "calendar-data"); } else { $reply->CalDAVElement($response, "request-status", "2.0;Success"); $reply->CalDAVElement($response, "calendar-data", $a); } $responses[] = $response; } continue; } if (!($attendee_usr = $qry->Fetch())) { $request->DoResponse(501, 'Database error'); } if ((privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0) { $reply->CalDAVElement($response, "request-status", "3.8;No authority"); $reply->CalDAVElement($response, "calendar-data"); $responses[] = $response; continue; } $attendee_path_match = '^/' . $attendee_usr->username . '/'; $fb = get_freebusy($attendee_path_match, $range_start, $range_end, bindec($attendee_usr->p)); $fb->AddProperty('UID', $ic->GetPValue('UID')); $fb->SetProperties($ic->GetProperties('ORGANIZER'), 'ORGANIZER'); $fb->AddProperty($attendee); $vcal = new vCalendar(array('METHOD' => 'REPLY')); $vcal->AddComponent($fb); $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav'); $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value())); $reply->CalDAVElement($response, "request-status", "2.0;Success"); // Cargo-cult setting $reply->CalDAVElement($response, "calendar-data", $vcal->Render()); $responses[] = $response; } $response = $reply->NewXMLElement("schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:caldav'); $request->XMLResponse(200, $response); }
/** * Expand the instances for a STANDARD or DAYLIGHT component of a VTIMEZONE * * @param object $vResource is a VCALENDAR with a VTIMEZONE containing components needing expansion * @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events. * @param object $range_end A RepeatRuleDateTime which is the end of the range for events. * @param int $offset_from The offset from UTC in seconds at the onset time. * * @return array of onset datetimes with UTC from/to offsets */ function expand_timezone_onsets(vCalendar $vResource, RepeatRuleDateTime $range_start, RepeatRuleDateTime $range_end) { global $c; $vtimezones = $vResource->GetComponents(); $vtz = $vtimezones[0]; $components = $vtz->GetComponents(); $instances = array(); $dtstart = null; $is_date = false; $has_repeats = false; $zone_tz = $vtz->GetPValue('TZID'); foreach ($components as $k => $comp) { if (DEBUG_EXPAND) { printf("Starting TZ expansion for component '%s' in timezone '%s'\n", $comp->GetType(), $zone_tz); foreach ($instances as $k => $v) { print ' : ' . $k; } print "\n"; } $dtstart_prop = $comp->GetProperty('DTSTART'); if (!isset($dtstart_prop)) { continue; } $dtstart = new RepeatRuleDateTime($dtstart_prop); $dtstart->setTimeZone('UTC'); $offset_from = $comp->GetPValue('TZOFFSETFROM'); $offset_from = $offset_from / 100 * 3600 + abs($offset_from) % 100 * 60 * ($offset_from < 0 ? -1 : 0); $offset_from *= -1; $offset_from = "{$offset_from} seconds"; dbg_error_log('tz/update', "%s of offset\n", $offset_from); $dtstart->modify($offset_from); $is_date = $dtstart->isDate(); $instances[$dtstart->UTC('Y-m-d\\TH:i:s\\Z')] = $comp; $rrule = $comp->GetProperty('RRULE'); $has_repeats = isset($rrule); if (!$has_repeats) { continue; } $recur = $comp->GetProperty('RRULE'); if (isset($recur)) { $recur = $recur->Value(); $this_start = clone $dtstart; $rule = new RepeatRule($this_start, $recur, $is_date); $i = 0; $result_limit = 1000; while ($date = $rule->next()) { $instances[$date->UTC('Y-m-d\\TH:i:s\\Z')] = $comp; if ($i++ >= $result_limit || $date > $range_end) { break; } } if (DEBUG_EXPAND) { print "After rrule_expand"; foreach ($instances as $k => $v) { print ' : ' . $k; } print "\n"; } } $properties = $comp->GetProperties('RDATE'); if (count($properties)) { foreach ($properties as $p) { $timezone = $p->GetParameterValue('TZID'); $rdate = $p->Value(); $rdates = explode(',', $rdate); foreach ($rdates as $k => $v) { $rdate = new RepeatRuleDateTime($v, $timezone, $is_date); if ($return_floating_times) { $rdate->setAsFloat(); } $instances[$rdate->UTC('Y-m-d\\TH:i:s\\Z')] = $comp; if ($rdate > $range_end) { break; } } } if (DEBUG_EXPAND) { print "After rdate_expand"; foreach ($instances as $k => $v) { print ' : ' . $k; } print "\n"; } } } ksort($instances); $onsets = array(); $start_utc = $range_start->UTC('Y-m-d\\TH:i:s\\Z'); $end_utc = $range_end->UTC('Y-m-d\\TH:i:s\\Z'); foreach ($instances as $utc => $comp) { if ($utc > $end_utc) { if (DEBUG_EXPAND) { printf("We're done: {$utc} is out of the range.\n"); } break; } if ($utc < $start_utc) { continue; } $onsets[$utc] = array('from' => $comp->GetPValue('TZOFFSETFROM'), 'to' => $comp->GetPValue('TZOFFSETTO'), 'name' => $comp->GetPValue('TZNAME'), 'type' => $comp->GetType()); } return $onsets; }
<?php /** * CalDAV Server - handle PUT method * * @package davical * @subpackage caldav * @author Andrew McMillan <*****@*****.**> * @copyright Catalyst .Net Ltd, Morphoss Ltd * @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later */ dbg_error_log("PUT", "method handler"); require_once 'DAVResource.php'; include_once 'caldav-PUT-functions.php'; $vcalendar = new vCalendar($request->raw_post); $uid = $vcalendar->GetUID(); if (empty($uid)) { $uid = uuid(); $vcalendar->SetUID($uid); } if ($add_member) { $request->path = $request->dav_name() . $uid . '.ics'; $dav_resource = new DAVResource($request->path); if ($dav_resource->Exists()) { $uid = uuid(); $vcalendar->SetUID($uid); $request->path = $request->dav_name() . $uid . '.ics'; $dav_resource = new DAVResource($request->path); if ($dav_resource->Exists()) { throw new Exception("Failed to generate unique segment name for add-member!"); }
/** * Parse out the attendee property and write a row to the * calendar_attendee table for each one. * @param int $dav_id The dav_id of the caldav_data we're processing * @param vComponent The VEVENT or VTODO containing the ATTENDEEs * @return null */ function WriteCalendarAttendees($dav_id, vCalendar $vcal) { $qry = new AwlQuery('DELETE FROM calendar_attendee WHERE dav_id = ' . $dav_id); $qry->Exec('PUT', __LINE__, __FILE__); $attendees = $vcal->GetAttendees(); if (count($attendees) < 1) { return; } $qry->SetSql('INSERT INTO calendar_attendee ( dav_id, status, partstat, cn, attendee, role, rsvp, params ) VALUES( ' . $dav_id . ', :status, :partstat, :cn, :attendee, :role, :rsvp, :params )'); $qry->Prepare(); $processed = array(); foreach ($attendees as $v) { $attendee = $v->Value(); if (isset($processed[$attendee])) { dbg_error_log('LOG', 'Duplicate attendee "%s" in resource "%d"', $attendee, $dav_id); dbg_error_log('LOG', 'Original: "%s"', $processed[$attendee]); dbg_error_log('LOG', 'Duplicate: "%s"', $v->Render()); continue; /** @todo work out why we get duplicate ATTENDEE on one VEVENT */ } $qry->Bind(':attendee', $attendee); $qry->Bind(':status', $v->GetParameterValue('STATUS')); $qry->Bind(':partstat', $v->GetParameterValue('PARTSTAT')); $qry->Bind(':cn', $v->GetParameterValue('CN')); $qry->Bind(':role', $v->GetParameterValue('ROLE')); $qry->Bind(':rsvp', $v->GetParameterValue('RSVP')); $qry->Bind(':params', $v->Parameters()); $qry->Exec('PUT', __LINE__, __FILE__); $processed[$attendee] = $v->Render(); } }
function handle_remote_attendee_reply(vCalendar $ical) { $attendees = $ical->GetAttendees(); // attendee reply have just one attendee if (count($attendees) != 1) { return; } $attendee = $attendees[0]; $uidparam = $ical->GetPropertiesByPath("VCALENDAR/*/UID"); $uid = $uidparam[0]->Value(); $parameters = $attendee->Parameters(); $propertyText = ''; foreach ($parameters as $key => $param) { if (!empty($propertyText)) { $propertyText .= ';'; } $propertyText .= $key . '=' . $param; } //$propertyText .= ':' . $attendee->Value(); $qry = new AwlQuery('SELECT dav_id, calendar_item.collection_id AS collection_id, calendar_item.dav_name AS dav_name, caldav_data FROM calendar_item LEFT JOIN caldav_data USING(dav_id) WHERE uid = :uid'); $qry->Bind(':uid', $uid); $qry->Exec('select dav_id, collection_id'); if ($row = $qry->Fetch()) { $qry = new AwlQuery('UPDATE calendar_attendee SET email_status=:statusTo, partstat=:partstat, params=:params WHERE attendee=:attendee AND dav_id = :dav_id'); // user accepted $qry->Bind(':statusTo', EMAIL_STATUS::NORMAL); $qry->Bind(':attendee', $attendee->Value()); $qry->Bind(':dav_id', $row->dav_id); $qry->Bind(':params', $propertyText); $qry->Bind(':partstat', $parameters['PARTSTAT']); $qry->Exec('changeStatusTo'); //'(SELECT dav_id FROM calendar_item WHERE uid = :uid)'; $collection_id = $row->collection_id; $dav_name = $row->dav_name; //$qry->QDo("SELECT write_sync_change( $collection_id, 200, :dav_name)", array(':dav_name' => $dav_name ) ); //$qry->Execute(); $this->update_caldav_data($row->caldav_data, $row->dav_id); } return true; }
$qry = new AwlQuery($sql, $params); if (!$qry->Exec()) { exit(1); } if ($qry->rows() < 1) { $sql = 'SELECT our_tzno, tzid, active, olson_name, vtimezone, etag, '; $sql .= 'to_char(last_modified,\'Dy, DD Mon IYYY HH24:MI:SS "GMT"\') AS last_modified '; $sql .= 'FROM timezones JOIN tz_aliases USING(our_tzno) WHERE tzalias=:tzid'; if (!$qry->Exec()) { exit(1); } if ($qry->rows() < 1) { $request->DoResponse(404); } } $tz = $qry->Fetch(); $vtz = new vCalendar($tz->vtimezone); $vtz->AddProperty('TZ-URL', $c->protocol_server_port . $_SERVER['REQUEST_URI']); $vtz->AddProperty('TZNAME', $tz->olson_name); if ($qry->QDo('SELECT * FROM tz_localnames WHERE our_tzno = :our_tzno', array(':our_tzno' => $tz->our_tzno)) && $qry->rows()) { while ($name = $qry->Fetch()) { if (strpos($_SERVER['QUERY_STRING'], 'lang=' . $name->locale) !== false) { $vtz->AddProperty('TZNAME', $name->localised_name, array('LANGUAGE', str_replace('_', '-', $name->locale))); } } } header('ETag: "' . $tz->etag . '"'); header('Last-Modified: ' . $tz->last_modified); header('Content-Disposition: Attachment; Filename="' . str_replace('/', '-', $tzid . '.ics"')); $request->DoResponse(200, $vtz->Render(), 'text/calendar; charset=UTF-8'); exit(0);
function ischedule_freebusy_request($ic, $attendees, $attendees_fail) { global $c, $session, $request; $reply = new XMLDocument(array("urn:ietf:params:xml:ns:ischedule" => "I")); $icalAttendees = $ic->GetAttendees(); $responses = array(); $ical = $ic->GetComponents('VFREEBUSY'); $ical = $ical[0]; $fbq_start = $ical->GetPValue('DTSTART'); $fbq_end = $ical->GetPValue('DTEND'); if (!(isset($fbq_start) || isset($fbq_end))) { $request->DoResponse(400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND'); } $range_start = new RepeatRuleDateTime($fbq_start); $range_end = new RepeatRuleDateTime($fbq_end); foreach ($attendees as $k => $attendee) { $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule'); $fb = get_freebusy('^' . $attendee->dav_name, $range_start, $range_end); $fb->AddProperty('UID', $ical->GetPValue('UID')); $fb->SetProperties($ical->GetProperties('ORGANIZER'), 'ORGANIZER'); foreach ($ical->GetProperties('ATTENDEE') as $at) { if ($at->Value() == 'mailto:' . $attendee->email) { $fb->AddProperty($at); } } $vcal = new vCalendar(array('METHOD' => 'REPLY')); $vcal->AddComponent($fb); $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule'); $response->NewElement("recipient", 'mailto:' . $attendee->email, false, 'urn:ietf:params:xml:ns:ischedule'); $response->NewElement("request-status", "2.0;Success", false, 'urn:ietf:params:xml:ns:ischedule'); $response->NewElement("calendar-data", $vcal->Render(), false, 'urn:ietf:params:xml:ns:ischedule'); $responses[] = $response; } foreach ($attendees_fail as $k => $attendee) { $XMLresponse = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule'); $XMLresponse->NewElement("recipient", $reply->href('mailto:' . $attendee)); $XMLresponse->NewElement("request-status", '5.3;cannot schedule this user, unknown or access denied'); $responses[] = $XMLresponse; } $response = $reply->NewXMLElement("schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:ischedule'); $request->XMLResponse(200, $response); }
/** * Send an iMIP message since they look like a non-local user. * * @param string $method The METHOD parameter from the iTIP * @param string $to_email The e-mail address we're going to send to * @param vCalendar $vcal The iTIP part of the message. */ function doImipMessage($method, $to_email, vCalendar $itip) { global $c, $request; header('Debug: Sending iMIP ' . $method . ' message to ' . $to_email); $mime = new MultiPart(); $mime->addPart($itip->Render(), 'text/calendar; charset=UTF-8; method=' . $method); $friendly_part = isset($c->iMIP->template[$method]) ? $c->iMIP->template[$method] : <<<EOTEMPLATE This is a meeting ##METHOD## which your e-mail program should be able to import into your calendar. Alternatively you could save the attachment and load that into your calendar instead. EOTEMPLATE; $components = $itip->GetComponents('VTIMEZONE', false); $replaceable = array('METHOD', 'DTSTART', 'DTEND', 'SUMMARY', 'DESCRIPTION', 'URL'); foreach ($replaceable as $pname) { $search = '##' . $pname . '##'; if (strstr($friendly_part, $search) !== false) { $property = $itip->GetProperty($pname); if (empty($property)) { $property = $components[0]->GetProperty($pname); } if (empty($property)) { $replace = ''; } else { switch ($pname) { case 'DTSTART': case 'DTEND': $when = new RepeatRuleDateTime($property); $replace = $when->format('c'); break; default: $replace = $property->GetValue(); } } $friendly_part = str_replace($search, $replace, $friendly_part); } } $mime->addPart($friendly_part, 'text/plain'); $email = new EMail(); $email->SetFrom($request->principal->email()); $email->AddTo($to_email); $email->SetSubject($components[0]->GetPValue('SUMMARY')); $email->SetBody($mime->getMimeParts()); if (isset($c->iMIP->pretend_email)) { $email->Pretend($mime->getMimeHeaders()); } else { if (!isset($c->iMIP->send_email) || !$c->iMIP->send_email) { $email->PretendLog($mime->getMimeHeaders()); } else { $email->Send($mime->getMimeHeaders()); } } }
if (preg_match('{^/(\\S+@[a-z0-9][a-z0-9-]*[.][a-z0-9.-]+)/?$}i', $request->path, $matches)) { $principal = new Principal('email', $matches[1]); $path_match = '^' . $principal->dav_name(); } if (isset($fb_format) && $fb_format != 'text/calendar') { $request->DoResponse(406, translate('This server only supports the text/calendar format for freebusy URLs')); } if (!$request->HavePrivilegeTo('read-free-busy')) { $request->DoResponse(404); } require_once "freebusy-functions.php"; switch ($_SERVER['REQUEST_METHOD']) { case 'GET': $range_start = new RepeatRuleDateTime($fb_start); if (!isset($fb_end)) { $range_end = clone $range_start; $range_end->modify($fb_period); } else { $range_end = new RepeatRuleDateTime($fb_end); } $freebusy = get_freebusy($path_match, $range_start, $range_end); $result = new vCalendar(); $result->AddComponent($freebusy); $request->DoResponse(200, $result->Render(), 'text/calendar'); break; default: dbg_error_log("freebusy", "Unhandled request method >>%s<<", $_SERVER['REQUEST_METHOD']); dbg_log_array("freebusy", 'HEADERS', $raw_headers); dbg_log_array("freebusy", '_SERVER', $_SERVER, true); @dbg_error_log("freebusy", "RAW: %s", str_replace("\n", "", str_replace("\r", "", $request->raw_post))); }
/** * Return XML for a single component from the DB * * @param array $properties The properties for this component * @param string $item The DB row data for this component * * @return string An XML document which is the response for the component */ function component_to_xml($properties, $item) { global $session, $c, $request, $reply; dbg_error_log("REPORT", "Building XML Response for item '%s'", $item->dav_name); $denied = array(); $unsupported = array(); $caldav_data = $item->caldav_data; $displayname = preg_replace('{^.*/}', '', $item->dav_name); $type = 'unknown'; $contenttype = 'text/plain'; switch (strtoupper($item->caldav_type)) { case 'VJOURNAL': case 'VEVENT': case 'VTODO': $displayname = $item->summary; $type = 'calendar'; $contenttype = 'text/calendar'; if (isset($properties['urn:ietf:params:xml:ns:caldav:calendar-data']) || isset($properties['DAV::displayname'])) { if (!$request->AllowedTo('all') && $session->user_no != $item->user_no) { // the user is not admin / owner of this calendar looking at his calendar and can not admin the other cal if ($item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read')) { dbg_error_log("REPORT", "Anonymising confidential event for: %s", $item->dav_name); $vcal = new vCalendar($caldav_data); $caldav_data = $vcal->Confidential()->Render(); $displayname = translate('Busy'); } } } if (isset($c->hide_alarm) && $c->hide_alarm) { $dav_resource = new DAVResource($item->dav_name); if (isset($properties['urn:ietf:params:xml:ns:caldav:calendar-data']) && !$dav_resource->HavePrivilegeTo('write')) { dbg_error_log("REPORT", "Stripping event alarms for: %s", $item->dav_name); $vcal = new vCalendar($caldav_data); $vcal->ClearComponents('VALARM'); $caldav_data = $vcal->Render(); } } break; case 'VCARD': $displayname = $item->fn; $type = 'vcard'; $contenttype = 'text/vcard'; break; } $url = ConstructURL($item->dav_name); $prop = new XMLElement("prop"); $need_resource = false; foreach ($properties as $full_tag => $v) { $base_tag = preg_replace('{^.*:}', '', $full_tag); switch ($full_tag) { case 'DAV::getcontentlength': $contentlength = strlen($caldav_data); $prop->NewElement($base_tag, $contentlength); break; case 'DAV::getlastmodified': $prop->NewElement($base_tag, ISODateToHTTPDate($item->modified)); break; case 'urn:ietf:params:xml:ns:caldav:calendar-data': if ($type == 'calendar') { $reply->CalDAVElement($prop, $base_tag, $caldav_data); } else { $unsupported[] = $base_tag; } break; case 'urn:ietf:params:xml:ns:carddav:address-data': if ($type == 'vcard') { $reply->CardDAVElement($prop, $base_tag, $caldav_data); } else { $unsupported[] = $base_tag; } break; case 'DAV::getcontenttype': $prop->NewElement($base_tag, $contenttype); break; case 'DAV::current-user-principal': $prop->NewElement("current-user-principal", $request->current_user_principal_xml); break; case 'DAV::displayname': $prop->NewElement($base_tag, $displayname); break; case 'DAV::resourcetype': $prop->NewElement($base_tag); // Just an empty resourcetype for a non-collection. break; case 'DAV::getetag': $prop->NewElement($base_tag, '"' . $item->dav_etag . '"'); break; case '"current-user-privilege-set"': $prop->NewElement($base_tag, privileges($request->permissions)); break; default: // It's harder. We need the DAVResource() to get this one. $need_resource = true; } if ($need_resource) { break; } } $href = new XMLElement("href", $url); if ($need_resource) { if (!isset($dav_resource)) { $dav_resource = new DAVResource($item->dav_name); } $elements = $dav_resource->GetPropStat(array_keys($properties), $reply); array_unshift($elements, $href); } else { $elements = array($href); $status = new XMLElement("status", "HTTP/1.1 200 OK"); $elements[] = new XMLElement("propstat", array($prop, $status)); if (count($denied) > 0) { $status = new XMLElement("status", "HTTP/1.1 403 Forbidden"); $noprop = new XMLElement("prop"); foreach ($denied as $k => $v) { $reply->NSElement($noprop, $v); } $elements[] = new XMLElement("propstat", array($noprop, $status)); } if (!$request->PreferMinimal() && count($unsupported) > 0) { $status = new XMLElement("status", "HTTP/1.1 404 Not Found"); $noprop = new XMLElement("prop"); foreach ($unsupported as $k => $v) { $reply->NSElement($noprop, $v); } $elements[] = new XMLElement("propstat", array($noprop, $status)); } } $response = new XMLElement("response", $elements); return $response; }