Пример #1
0
 /**
  * Send a need-privileges error response.  This function will only return
  * if the $href is not supplied and the current user has the specified
  * permission for the request path.
  *
  * @param string $privilege The name of the needed privilege.
  * @param string $href The unconstructed URI where we needed the privilege.
  */
 function NeedPrivilege($privileges, $href = null)
 {
     if (is_string($privileges)) {
         $privileges = array($privileges);
     }
     if (!isset($href)) {
         if ($this->HavePrivilegeTo($privileges)) {
             return;
         }
         $href = $this->path;
     }
     $reply = new XMLDocument(array('DAV:' => ''));
     $privnodes = array($reply->href(ConstructURL($href)), new XMLElement('privilege'));
     // RFC3744 specifies that we can only respond with one needed privilege, so we pick the first.
     $reply->NSElement($privnodes[1], $privileges[0]);
     $xml = new XMLElement('need-privileges', new XMLElement('resource', $privnodes));
     $xmldoc = $reply->Render('error', $xml);
     $this->DoResponse(403, $xmldoc, 'text/xml; charset="utf-8"');
     exit(0);
     // Unecessary, but might clarify things
 }
Пример #2
0
            }
            break;
    }
}
if ($ticket_timeout == 'infinity') {
    $sql_timeout = null;
} else {
    if (preg_match('{^([a-z]+)-(\\d+)$}i', $ticket_timeout, $matches)) {
        /** It isn't specified, but timeout seems to be 'unit-number' like 'Seconds-3600', so we make it '3600 Seconds' which PostgreSQL understands */
        $sql_timeout = $matches[2] . ' ' . $matches[1];
    } else {
        $sql_timeout = $ticket_timeout;
    }
}
$collection_id = $target->GetProperty('collection_id');
$resource_id = $target->GetProperty('dav_id');
$i = 0;
do {
    $ticket_id = substr(str_replace('/', '', str_replace('+', '', base64_encode(sha1(date('r') . rand(0, 2100000000) . microtime(true), true)))), 7, 8);
    $qry = new AwlQuery('INSERT INTO access_ticket ( ticket_id, dav_owner_id, privileges, target_collection_id, target_resource_id, expires )
                VALUES( :ticket_id, :owner, :privs::INT::BIT(24), :collection, :resource, (current_timestamp + :expires::interval) )', array(':ticket_id' => $ticket_id, ':owner' => $session->principal_id, ':privs' => $ticket_privileges, ':collection' => $collection_id, ':resource' => $resource_id, ':expires' => $sql_timeout));
    $result = $qry->Exec('MKTICKET', __LINE__, __FILE__);
} while (!$result && $i++ < 2);
$privs = new XMLElement('privilege');
foreach (bits_to_privilege($ticket_privileges) as $k => $v) {
    $reply->NSElement($privs, $v);
}
$ticketinfo = new XMLElement('T:ticketinfo', array(new XMLElement('T:id', $ticket_id), new XMLElement('owner', $reply->href(ConstructURL('/' . $session->username . '/'))), $privs, new XMLElement('T:timeout', $ticket_timeout), new XMLElement('T:visits', 'infinity')));
$prop = new XMLElement("prop", new XMLElement('T:ticketdiscovery', $ticketinfo), $reply->GetXmlNsArray());
header('Ticket: ' . $ticket_id);
$request->XMLResponse(200, $prop);
Пример #3
0
/**
* 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');
}
Пример #4
0
    }
    /**
     * If we have encountered any instances of failure, the whole damn thing fails.
     */
    if (count($failure) > 0) {
        $props = array();
        $status = array();
        foreach ($success as $tag => $v) {
            // Unfortunately although these succeeded, we failed overall, so they didn't happen...
            $props[] = new XMLElement($reply->Tag($tag));
        }
        $status[] = new XMLElement('propstat', array(new XMLElement('prop', $props), new XMLElement('status', 'HTTP/1.1 424 Failed Dependency')));
        if ($request_type == 'extended-mkcol') {
            $request->DoResponse($failure_code, $reply->Render('mkcol-response', array_merge($status, $failure), 'text/xml; charset="utf-8"'));
        } else {
            array_unshift($failure, $reply->href(ConstructURL($request->path)));
            $failure[] = new XMLElement('responsedescription', translate('Some properties were not able to be set.'));
            $request->DoResponse(207, $reply->Render('multistatus', new XMLElement('response', $failure)), 'text/xml; charset="utf-8"');
        }
    }
}
$sql = 'SELECT * FROM collection WHERE dav_name = :dav_name';
$qry = new AwlQuery($sql, array(':dav_name' => $request->path));
if (!$qry->Exec('MKCOL', __LINE__, __FILE__)) {
    $request->DoResponse(500, translate('Error querying database.'));
}
if ($qry->rows() != 0) {
    $request->DoResponse(405, translate('A collection already exists at that location.'));
}
$qry = new AwlQuery();
$qry->Begin();
Пример #5
0
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);
}
Пример #6
0
/**
* Deliver scheduling replies to organizer and other attendees
* @param iCalComponent $ical the VCALENDAR to deliver
* @return false on error
*/
function handle_schedule_reply($ical)
{
    global $c, $session, $request;
    $resources = $ical->GetComponents('VTIMEZONE', false);
    $ic = $resources[0];
    $etag = md5($request->raw_post);
    $organizer = $ic->GetProperties('ORGANIZER');
    // for now we treat events with out organizers as an error
    if (count($organizer) < 1) {
        return false;
    }
    $attendees = array_merge($organizer, $ic->GetProperties('ATTENDEE'));
    $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
    if (count($wr_attendees) > 0) {
        dbg_error_log("POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property");
        foreach ($wr_attendees as $k => $v) {
            $attendees[] = $v;
        }
    }
    dbg_error_log("POST", "Attempting to deliver scheduling request for %d attendees", count($attendees));
    foreach ($attendees as $k => $attendee) {
        $attendee_email = preg_replace('/^mailto:/', '', $attendee->Value());
        dbg_error_log("POST", "Delivering to %s", $attendee_email);
        $attendee_principal = new CalDAVPrincipal(array('email' => $attendee_email, 'options' => array('allow_by_email' => true)));
        $deliver_path = preg_replace('/^.*caldav.php/', '', $attendee_principal->schedule_inbox_url);
        $attendee_email = preg_replace('/^mailto:/', '', $attendee->Value());
        if ($attendee_email == $request->principal->email) {
            dbg_error_log("POST", "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(ConstructURL($attendee_principal->schedule_inbox_url)), 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 iCalComponent();
        $ncal->VCalendar();
        $ncal->AddProperty('METHOD', 'REPLY');
        $ncal->AddComponent(array_merge($ical->GetComponents('VEVENT', false), array($ic)));
        $content = $ncal->Render();
        write_resource($attendee_principal->user_no, $deliver_path . $etag . '.ics', $content, $ar->GetProperty('collection_id'), $request->user_no, md5($content), $ncal, $put_action_type = 'INSERT', $caldav_context = true, $log_action = true, $etag);
    }
    $request->DoResponse(201, 'Created');
}
Пример #7
0
function ischedule_cancel($ic, $attendees, $attendees_fail)
{
    global $c, $session, $request;
    $reply = new XMLDocument(array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C", "urn:ietf:params:xml:ns:ischedule" => "I"));
    $responses = array();
    $ical = $ic->GetComponents('VEVENT');
    $ical = $ical[0];
    foreach ($attendees as $k => $attendee) {
        $XMLresponse = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule');
        dbg_error_log('ischedule', 'scheduling event for ' . $attendee->email);
        $schedule_target = new Principal('email', $attendee->email);
        $response = '3.7';
        // Attendee was not found on server.
        if ($schedule_target->Exists()) {
            $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.3;cannot schedule this user, unknown or access denied';
                // 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;denied';
                    //  No authority to deliver invitations to user.
                } else {
                    if ($attendee_inbox->WriteCalendarMember($ic, false) !== false) {
                        $response = '2.0;delivered';
                        // Scheduling invitation delivered successfully
                    }
                }
            }
        }
        dbg_error_log('PUT', 'Status for attendee <%s> set to "%s"', $attendee->email, $response);
        $XMLresponse->NewElement("recipient", $reply->href('mailto:' . $attendee->email), false, 'urn:ietf:params:xml:ns:ischedule');
        $XMLresponse->NewElement("request-status", $response, false, 'urn:ietf:params:xml:ns:ischedule');
        $responses[] = $XMLresponse;
    }
    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->email), false, 'urn:ietf:params:xml:ns:ischedule');
        $XMLresponse->NewElement("request-status", '5.3;cannot schedule this user, unknown or access denied', false, 'urn:ietf:params:xml:ns:ischedule');
        $responses[] = $XMLresponse;
    }
    $response = $reply->NewXMLElement("schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:ischedule');
    $request->XMLResponse(200, $response);
}