function SRVOk($value, $name, $row) { global $BrowserCurrentRow; if (empty($BrowserCurrentRow->domain)) { return ''; } // skip empty rows $s = new iSchedule(); $s->domain = $BrowserCurrentRow->domain; return translate($s->getServer() ? 'OK' : SRVFormat($s->domain)); }
/** * Create/Update the scheduling requests for this resource. This includes updating * the scheduled user's default calendar. * @param vComponent $resource The VEVENT/VTODO/... resource we are scheduling * @param boolean $create true if the scheduling requests are being created. * @return true If there was any scheduling action */ function do_scheduling_requests(vCalendar $resource, $create, $old_data = null, $remoteAttendee = false) { global $request, $c; if (!isset($request) || isset($c->enable_auto_schedule) && !$c->enable_auto_schedule) { return false; } if (!is_object($resource)) { trace_bug('do_scheduling_requests called with non-object parameter (%s)', gettype($resource)); return false; } $organizer = $resource->GetOrganizer(); if ($organizer === false || empty($organizer)) { dbg_error_log('PUT', 'Event has no organizer - no scheduling required.'); return false; } $organizer_email = preg_replace('/^mailto:/i', '', $organizer->Value()); if ($request->principal->email() != $organizer_email) { return do_scheduling_reply($resource, $organizer); } $schedule_request = clone $resource; $schedule_request->AddProperty('METHOD', 'REQUEST'); $old_attendees = array(); if (!empty($old_data)) { $old_resource = new vCalendar($old_data); $old_attendees = $old_resource->GetAttendees(); } $attendees = $resource->GetAttendees(); if (count($attendees) == 0 && count($old_attendees) == 0) { dbg_error_log('PUT', 'Event has no attendees - no scheduling required.', count($attendees)); return false; } $removed_attendees = array(); foreach ($old_attendees as $attendee) { $email = preg_replace('/^mailto:/i', '', $attendee->Value()); if ($email == $request->principal->email()) { continue; } $removed_attendees[$email] = $attendee; } $uids = $resource->GetPropertiesByPath('/VCALENDAR/*/UID'); if (count($uids) == 0) { dbg_error_log('PUT', 'No UID in VCALENDAR - giving up on REPLY.'); return false; } $uid = $uids[0]->Value(); dbg_error_log('PUT', 'Writing scheduling resources for %d attendees', count($attendees)); $scheduling_actions = false; foreach ($attendees as $attendee) { $email = preg_replace('/^mailto:/i', '', $attendee->Value()); if ($email == $request->principal->email()) { dbg_error_log("PUT", "not delivering to owner '%s'", $request->principal->email()); continue; } if ($create) { $attendee_is_new = true; } else { $attendee_is_new = !isset($removed_attendees[$email]); if (!$attendee_is_new) { unset($removed_attendees[$email]); } } $agent = $attendee->GetParameterValue('SCHEDULE-AGENT'); if ($agent && $agent != 'SERVER') { dbg_error_log("PUT", "not delivering to %s, schedule agent set to value other than server", $email); continue; } $schedule_target = new Principal('email', $email); $response = '3.7'; // Attendee was not found on server. dbg_error_log('PUT', 'Handling scheduling resources for %s on %s which is %s', $email, $create ? 'create' : 'update', $attendee_is_new ? 'new' : 'an update'); if ($schedule_target->Exists()) { // Instead of always writing to schedule-default-calendar, we first try to // find a calendar with an existing instance of the event. $sql = 'SELECT caldav_data.dav_name, caldav_data.caldav_data, caldav_data.collection_id FROM caldav_data JOIN calendar_item USING(dav_id) '; $sql .= 'WHERE caldav_data.collection_id IN (SELECT collection_id FROM collection WHERE is_calendar AND user_no =?) '; $sql .= 'AND uid=? LIMIT 1'; $qry = new AwlQuery($sql, $schedule_target->user_no(), $uid); if (!$qry->Exec('PUT', __LINE__, __FILE__) || $qry->rows() < 1) { dbg_error_log('PUT', "Could not find event in attendee's calendars"); $attendee_calendar = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-default-calendar'))); } else { $row = $qry->Fetch(); $r = new DAVResource($row); $attendee_calendar = new WritableCollection(array('path' => $r->parent_path())); if ($attendee_calendar->IsCalendar()) { dbg_error_log('XXX', "found the event in attendee's calendar %s", $attendee_calendar->dav_name()); } else { dbg_error_log('XXX', 'could not find the event in any calendar, using schedule-default-calendar'); $attendee_calendar = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-default-calendar'))); } } if (!$attendee_calendar->Exists()) { dbg_error_log('ERROR', 'Default calendar at "%s" does not exist for user "%s"', $attendee_calendar->dav_name(), $schedule_target->username()); $response = '5.2'; // No scheduling support for user } else { $attendee_inbox = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-inbox'))); if (!$attendee_inbox->HavePrivilegeTo('schedule-deliver-invite')) { $response = '3.8'; // No authority to deliver invitations to user. } else { if ($attendee_inbox->WriteCalendarMember($schedule_request, $attendee_is_new) !== false) { $response = '1.2'; // Scheduling invitation delivered successfully if ($attendee_calendar->WriteCalendarMember($resource, $attendee_is_new) === false) { dbg_error_log('ERROR', 'Could not write %s calendar member to %s', $attendee_is_new ? 'new' : 'updated', $attendee_calendar->dav_name(), $attendee_calendar->dav_name(), $schedule_target->username()); trace_bug('Failed to write scheduling resource.'); } } } } } else { if ($remoteAttendee) { $attendee->is_remote = true; $remote = new iSchedule(); $answer = $remote->sendRequest($email, 'VEVENT/REQUEST', $schedule_request->Render()); } else { $remote = new iSchedule(); $answer = $remote->sendRequest($email, 'VEVENT/REQUEST', $schedule_request->Render()); if ($answer === false) { $response = "3.7;Invalid Calendar User"; } else { foreach ($answer as $a) { if ($a === false) { $response = "3.7;Invalid Calendar User"; } elseif (substr($a, 0, 1) >= 1) { $response = $a; } else { $response = "2.0;Success"; } } } } } dbg_error_log('PUT', 'Status for attendee <%s> set to "%s"', $attendee->Value(), $response); $attendee->SetParameterValue('SCHEDULE-STATUS', $response); $scheduling_actions = true; } if (!$create) { foreach ($removed_attendees as $attendee) { $schedule_target = new Principal('email', $email); if ($schedule_target->Exists()) { $attendee_calendar = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-default-calendar'))); } } } return $scheduling_actions; }
global $request; if (isset($_SERVER['HTTP_DKIM_SIGNATURE'])) { $sig = $_SERVER['HTTP_DKIM_SIGNATURE']; } else { $request->DoResponse(403, translate('DKIM signature missing')); } $err = $this->parseDKIM($sig); if ($err !== true || $this->failed) { $request->DoResponse(403, translate('DKIM signature invalid ') . "\n" . $err . "\n" . $sig); } if (!$this->getTxt() || $this->failed) { $request->DoResponse(403, translate('DKIM signature validation failed(DNS ERROR)')); } if (!$this->parseTxt() || $this->failed) { $request->DoResponse(403, translate('DKIM signature validation failed(KEY Parse ERROR)')); } if (!$this->validateKey() || $this->failed) { $request->DoResponse(403, translate('DKIM signature validation failed(KEY Validation ERROR)')); } if (!$this->verifySignature() || $this->failed) { $request->DoResponse(403, translate('DKIM signature validation failed(Signature verification ERROR)') . $this->verifySignature()); } return true; } } $d = new iSchedule(); if ($d->validateRequest()) { include 'caldav-POST.php'; // TODO // handle request. }
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); }