/** * 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 }
/** * 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(); }
break; } } /** * 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.')); }
/** * 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 (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()) { // Uncache anything to do with the collection $cache_ns = 'collection-' . $dav_resource->dav_name(); } }
/** * 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'); }
continue; } $onsets[$utc] = array('from' => $comp->GetPValue('TZOFFSETFROM'), 'to' => $comp->GetPValue('TZOFFSETTO'), 'name' => $comp->GetPValue('TZNAME'), 'type' => $comp->GetType()); } return $onsets; } header('ETag: "' . $tz->etag . '"'); header('Last-Modified', $tz->last_modified); header('Content-Type: application/xml; charset="utf-8"'); $vtz = new vCalendar($tz->vtimezone); $response = new XMLDocument(array("urn:ietf:params:xml:ns:timezone-service" => "")); $timezones = $response->NewXMLElement('urn:ietf:params:xml:ns:timezone-service:timezones'); $qry = new AwlQuery('SELECT to_char(max(last_modified),\'YYYY-MM-DD"T"HH24:MI:SS"Z"\') AS dtstamp FROM timezones'); if ($qry->Exec('tz/list', __LINE__, __FILE__) && $qry->rows() > 0) { $row = $qry->Fetch(); $timezones->NewElement('dtstamp', $row->dtstamp); } else { $timezones->NewElement('dtstamp', gmdate('Y-m-d\\TH:i:s\\Z')); } $from = new RepeatRuleDateTime($start); $until = new RepeatRuleDateTime($end); $observances = expand_timezone_onsets($vtz, $from, $until); $tzdata = array(); $tzdata[] = new XMLElement('tzid', $tzid); $tzdata[] = new XMLElement('calscale', 'Gregorian'); foreach ($observances as $onset => $details) { $tzdata[] = new XMLElement('observance', array(new XMLElement('name', empty($details['name']) ? $details['type'] : $details['name']), new XMLElement('onset', $onset), new XMLElement('utc-offset-from', substr($details['from'], 0, -2) . ':' . substr($details['from'], -2)), new XMLElement('utc-offset-to', substr($details['to'], 0, -2) . ':' . substr($details['to'], -2)))); } $timezones->NewElement('tzdata', $tzdata); echo $response->Render($timezones); exit(0);
if ($qry->Exec('tz/list', __LINE__, __FILE__) && $qry->rows() > 0) { while ($tz = $qry->Fetch()) { $elements = array(new XMLElement('tzid', $tz->tzid), new XMLElement('last-modified', $tz->last_modified)); if ($tz->active != 't') { $elements[] = new XMLElement('inactive'); } if ($tz->tzid != $tz->olson_name) { $elements[] = new XMLElement('alias', $tz->olson_name); } if ($q2->QDo('SELECT * FROM tz_aliases WHERE our_tzno = ?', array($tz->our_tzno))) { while ($alias = $q2->Fetch()) { $elements[] = new XMLElement('alias', $alias->tzalias); } } if (!empty($lang) && $q2->QDo('SELECT * FROM tz_localnames WHERE our_tzno = ? AND locale = ?', array($tz->our_tzno, $lang)) && $q2->rows() > 0) { while ($local = $q2->Fetch()) { $attr = array('lang' => $local->locale); if ($local->preferred == 't') { $attr['preferred'] = 'true'; } $elements[] = new XMLElement('local-name', $local->localised_name, $attr); } } else { $elements[] = new XMLElement('local-name', $tz->tzid, empty($lang) ? null : array('lang' => $lang)); } $tzlist->NewElement('summary', $elements); } } header('Content-Type: application/xml; charset="utf-8"'); echo $response->Render($tzlist); exit(0);
/** * 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; } }