} 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);
/** * Get a single item from the server. * * @param string $url The URL to PROPFIND on */ function DoPROPFINDRequest($url, $props, $depth = 0) { $this->SetDepth($depth); $xml = new XMLDocument(array('DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C')); $prop = new XMLElement('prop'); foreach ($props as $v) { $xml->NSElement($prop, $v); } $this->body = $xml->Render('propfind', $prop); $this->requestMethod = 'PROPFIND'; $this->SetContentType('text/xml'); $this->DoRequest($url); return $this->GetXmlResponse(); }
/** * Get a single item from the server. * * @param string $url The URL to PROPFIND on */ function DoPROPFINDRequest($url, $props, $depth = 0) { $this->SetDepth($depth); $xml = new XMLDocument(array('DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C')); $prop = new XMLElement('prop'); foreach ($props as $v) { $xml->NSElement($prop, $v); } $this->DoRequest($url, "PROPFIND", $xml->Render('propfind', $prop), "text/xml"); return $this->xmlResponse; }
/** * 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 }
/** * 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'); }
} } /** * If we have encountered any instances of failure, the whole damn thing fails. */ if (count($failure) > 0) { $qry->Rollback(); $url = ConstructURL($request->path); $multistatus = new XMLElement('multistatus'); array_unshift($failure, new XMLElement('responsedescription', translate("Some properties were not able to be changed."))); array_unshift($failure, new XMLElement('href', $url)); $response = $reply->DAVElement($multistatus, 'response', $failure); if (!empty($success)) { $prop = new XMLElement('prop'); foreach ($success as $tag => $v) { $reply->NSElement($prop, $tag); } $reply->DAVElement($response, 'propstat', array($prop, new XMLElement('status', 'HTTP/1.1 424 Failed Dependency'))); } $request->DoResponse(207, $reply->Render($multistatus), 'text/xml; charset="utf-8"'); } /** * Otherwise we will try and do the SQL. This is inside a transaction, so PostgreSQL guarantees the atomicity */ if ($qry->Commit()) { $cache = getCacheInstance(); $cache_ns = null; if ($dav_resource->IsPrincipal()) { $cache_ns = 'principal-' . $dav_resource->dav_name(); } else { if ($dav_resource->IsCollection()) {
/** * 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'); }
/** * Applies a properties change to a DAV resource * * @return boolean TRUE on successful creation, i18n array (msg, * [params]) otherwise */ function proppatch($user, $passwd, $calendar = '', $props = array()) { $this->prepare_client($user, $passwd, ''); // Preconditions $logmsg = ''; $usermsg = ''; $params = array(); // Empty calendar? if (empty($calendar)) { $logmsg = 'no internal name specified'; $usermsg = 'error_internalcalnamemissing'; } if (!isset($props['displayname'])) { $logmsg = 'no display name specified'; $usermsg = 'error_calnamemissing'; } if (!isset($props['color'])) { $logmsg = 'no color specified'; $usermsg = 'error_calcolormissing'; } if (!empty($logmsg)) { $this->CI->extended_logs->message('ERROR', 'Invalid call to proppatch(): ' . $logmsg); return array($usermsg, $params); } $url = $this->build_calendar_url($user, $calendar); // Create XML body $ns = array('DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C', 'http://apple.com/ns/ical/' => 'ical'); $xml = new XMLDocument($ns); $set = $xml->NewXMLElement('set'); $prop = $set->NewElement('prop'); $xml->NSElement($prop, 'displayname', $props['displayname']); $xml->NSElement($prop, 'http://apple.com/ns/ical/:calendar-color', $props['color']); // TODO: associate timezone? AWL doesn't like <CDATA, // gets replaced by html entity $xml_text = $xml->Render('propertyupdate', $set, null, 'http://apple.com/ns/ical/:calendar-color'); $result = $this->client->DoPROPPATCH($xml_text, $url); $success = FALSE; $logmsg = ''; $usermsg = ''; if ($result === TRUE) { $success = TRUE; } else { $logmsg = $result; $usermsg = 'error_modfailed'; } if ($success === FALSE) { $this->CI->extended_logs->message('INTERNALS', 'Calendar ' . $calendar . ' not modified.' . ' Found unexpected status on some properties: ' . $logmsg); return array($usermsg, $params); } else { $this->CI->extended_logs->message('INTERNALS', 'Calendar ' . $calendar . ' successfully modified'); return TRUE; } }