/** * Deliver scheduling replies to organizer and other attendees * @param vComponent $ical the VCALENDAR to deliver * @return false on error */ function handle_schedule_reply(vCalendar $ical) { global $c, $session, $request; $resources = $ical->GetComponents('VTIMEZONE', false); $ic = $resources[0]; $etag = md5($request->raw_post); $organizer = $ical->GetOrganizer(); // for now we treat events with out organizers as an error if (empty($organizer)) { return false; } $att = $ical->GetAttendees(); $attendees = array_merge($organizer, $att); dbg_error_log("PUT", "Attempting to deliver scheduling request for %d attendees", count($attendees)); foreach ($attendees as $k => $attendee) { $attendee_email = preg_replace('/^mailto:/i', '', $attendee->Value()); dbg_error_log("PUT", "Delivering to %s", $attendee_email); $attendee_principal = new DAVPrincipal(array('email' => $attendee_email, 'options' => array('allow_by_email' => true))); $deliver_path = $attendee_principal->internal_url('schedule_inbox'); $attendee_email = preg_replace('/^mailto:/i', '', $attendee->Value()); if ($attendee_email == $request->principal->email) { dbg_error_log("PUT", "not delivering to owner"); continue; } $ar = new DAVResource($deliver_path); if (!$ar->HavePrivilegeTo('schedule-deliver-reply')) { $reply = new XMLDocument(array('DAV:' => '')); $privnodes = array($reply->href($attendee_principal->url('schedule_inbox')), new XMLElement('privilege')); // RFC3744 specifies that we can only respond with one needed privilege, so we pick the first. $reply->NSElement($privnodes[1], 'schedule-deliver-reply'); $xml = new XMLElement('need-privileges', new XMLElement('resource', $privnodes)); $xmldoc = $reply->Render('error', $xml); $request->DoResponse(403, $xmldoc, 'text/xml; charset="utf-8"'); continue; } $ncal = new vCalendar(array('METHOD' => 'REPLY')); $ncal->AddComponent(array_merge($ical->GetComponents('VEVENT', false), array($ic))); $content = $ncal->Render(); write_resource(new DAVResource($deliver_path . $etag . '.ics'), $content, $ar, $request->user_no, md5($content), $put_action_type = 'INSERT', $caldav_context = true, $log_action = true, $etag); } $request->DoResponse(201, 'Created'); }
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); }
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); }
/** * @param $row - array with : * // minimum for invitation http://www.ietf.org/rfc/rfc5546.txt [Page 20] * SUMMARY * DTSTAMP * DTSTART * DTEND * UID * * // extra params: * * * @param $organizer - array of organizer contain email as attendee and fullname as property column * @param $attendees - array of arrays with attendees (attendee, property) * @return string */ private function renderRowToInvitation($row, $organizer, $attendees) { $status = 'TENTATIVE'; $calendar = new vCalendar(); $calendar->AddProperty("METHOD", "REQUEST"); $event = new vComponent(); $event->SetType("VEVENT"); $event->AddProperty("SUMMARY", $row->summary); $event->AddProperty("DTSTAMP", $row->dtstamp); $event->AddProperty("DTSTART", $row->dtstart); $event->AddProperty("DTEND", $row->dtend); $event->AddProperty("UID", $row->uid); $event->AddProperty("EMAIL", $organizer->attendee); // url //$event->AddProperty("URL", "http://127.0.0.1/public.php?XDEBUG_SESSION_START=14830"); $organizerproperty = null; if (isset($organizer->params) && $organizer->params != null) { $organizerproperty = array('CN' => $organizer->params); } $event->AddProperty("ORGANIZER", 'mailto:' . $organizer->attendee, $organizerproperty); $event->AddProperty("STATUS", $status); foreach ($attendees as $attendee) { $partstat = $attendee->partstat; $attendeePropertyArray = $this->extractParametersToArrayFromProperty($attendee->params); // add partstat from DB $attendeePropertyArray['PARTSTAT'] = $partstat; $event->AddProperty("ATTENDEE", $attendee->attendee, $attendeePropertyArray); } $calendar->AddComponent($event); $result = $calendar->render(); return $result; }
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))); }