* @copyright 2006 The PHP Group * @license PHP License 3.0 http://www.php.net/license/3_0.txt * @version CVS: $Id: Server.php,v 1.4 2006/04/13 22:33:13 jablko Exp $ * @link http://pear.php.net/package/HTTP_CalDAV_Server * @see HTTP_WebDAV_Server */ require_once 'Tools/ReportParser.php'; /** * CalDav Server class * * Long description * * @category HTTP * @package HTTP_CalDAV_Server * @author Jack Bates * @copyright 2006 The PHP Group * @license PHP License 3.0 http://www.php.net/license/3_0.txt * @version CVS: $Id: Server.php,v 1.4 2006/04/13 22:33:13 jablko Exp $ * @link http://pear.php.net/package/HTTP_CalDAV_Server * @see HTTP_WebDAV_Server */ class HTTP_CalDAV_Server extends HTTP_WebDAV_Server { /** * Make a property in the CalDAV namespace * * @param string property name * @param string property value * @return array string property namespace * string property name * string property value */ function calDavProp($name, $value=null, $status=null) { return $this->mkprop('urn:ietf:params:xml:ns:caldav', $name, $value, $status); } /** * REPORT request helper - prepares data-structures from REPORT requests * * @param options * @return void * @access public */ function report_request_helper(&$options) { $options = array(); $options['path'] = $this->path; $options['depth'] = 'infinity'; if (isset($_SERVER['HTTP_DEPTH'])) { $options['depth'] = $_SERVER['HTTP_DEPTH']; } $parser = new ReportParser('php://input'); if (!$parser->success) { $this->http_status('400 Bad Request'); return; } $options['props'] = $parser->props; $options['filters'] = $parser->filters; return true; } /** * REPORT method wrapper * * @param void * @return void * @access public */ function report_wrapper() { /* Prepare data-structure from REPORT request */ if (!$this->report_request_helper($options)) { return; } /* Call user handler */ if (method_exists($this, 'report')) { if (!$this->report($options, $files)) { return; } } else { /* Empulate REPORT using PROPFIND */ if (!$this->propfind($options, $files)) { return; } } /* Format REPORT response */ // TODO Make ns_hash a class variable so we can prettify C: // Or make getNsName so we can return C: $this->propfind_response_helper($options, $files); } function getProp($reqprop, $file, $options) { // check if property exists in response foreach ($file['props'] as $prop) { if ($reqprop['name'] == $prop['name'] && $reqprop['ns'] == $prop['ns']) { return $prop; } } if ($reqprop['name'] == 'lockdiscovery' && $reqprop['ns'] == 'DAV:' && method_exists($this, 'getLocks')) { return $this->mkprop('DAV:', 'lockdiscovery', $this->getLocks($file['path'])); } if ($reqprop['name'] == 'calendar-data' && $reqprop['ns'] == 'urn:ietf:params:xml:ns:caldav' && method_exists($this, 'get')) { $filters = $options['filters']; $options = array(); $options['path'] = $file['path']; $status = $this->get($options); if (empty($status)) { $status = '403 Forbidden'; } if ($status !== true) { return $this->calDavProp('calendar-data', null, $status); } if ($options['mimetype'] != 'text/calendar') { return $this->calDavProp('calendar-data', null, '404 Not Found'); } if ($options['stream']) { $handle = $options['stream']; } else if ($options['data']) { // What about data? } else { return $this->calDavProp('calendar-data', null, '404 Not Found'); } if (!($value = $this->_parseComponent($handle, $reqprop['value'], $filters))) { return $this->calDavProp('calendar-data', null, '404 Not Found'); } return HTTP_CalDAV_Server::calDavProp('calendar-data', $value); } // incase the requested property had a value, like calendar-data unset($reqprop['value']); $reqprop['status'] = '404 Not Found'; return $reqprop; } function _parseComponent($handle, $value=null, $filters=null) { $comps = array(); $compValues = array($value); $compFilters = array($filters); while (($line = fgets($handle, 4096)) !== false) { $line = explode(':', trim($line)); if ($line[0] == 'END') { if ($line[1] != $comps[count($comps) - 1]->name) { return; } if (is_array($compFilters[count($compFilters) - 1]['filters'])) { foreach ($compFilters[count($compFilters) - 1]['filters'] as $filter) { if ($filter['name'] == 'time-range') { if ($filter['value']['start'] > $comps[count($comps) - 1]->properties['DTEND'][0]->value || $filter['value']['end'] < $comps[count($comps) - 1]->properties['DTSTART'][0]->value) { array_pop($compValues); array_pop($compFilters); continue; } } } } // If we're about to pop the root component, we ignore the rest // of our input if (count($comps) == 1) { return array_pop($comps); } if (!$comps[count($comps) - 2]->add_component(array_pop($comps))) { return; } array_pop($compValues); array_pop($compFilters); continue; } if ($line[0] == 'BEGIN') { $compName = $line[1]; if (is_array($compValues[count($compValues) - 1]['comps']) && !isset($compValues[count($compValues) - 1]['comps'][$compName])) { while (($line = fgets($handle, 4096)) !== false) { if (trim($line) == "END:$compName") { continue (2); } } return; } $className = 'iCalendar_' . ltrim(strtolower($compName), 'v'); if ($line[1] == 'VCALENDAR') { $className = 'iCalendar'; } if (!class_exists($className)) { while (($line = fgets($handle, 4096)) !== false) { if (trim($line) == "END:$compName") { continue (2); } } return; } $comps[] = new $className; $compValues[] = $compValues[count($compValues) - 1]['comps'][$compName]; $compFilters[] = $compFilters[count($compFilters) - 1]['comps'][$compName]; continue; } $line[0] = explode(';=', $line[0]); $propName = array_shift($line[0]); if (is_array($compValues[count($compValues) - 1]['props']) && !in_array($propName, $compValues[count($compValues) - 1]['props'])) { continue; } $params = array(); while (!empty($line[0])) { $params[array_shift($line[0])] = array_shift($line[0]); } $comps[count($comps) - 1]->add_property($propName, $line[1], $params); } } function _multistatus($responses) { // now we generate the response header... $this->http_status('207 Multi-Status'); header('Content-Type: text/xml; charset="utf-8"'); // ...and payload echo "\n"; echo "\n"; foreach ($responses as $response) { // ignore empty or incomplete entries if (!is_array($response) || empty($response)) { continue; } $ns_defs = array(); foreach ($response['ns_hash'] as $name => $prefix) { $ns_defs[] = "xmlns:$prefix=\"$name\""; } echo ' \n"; echo " $response[href]\n"; // report all found properties and their values (if any) // nothing to do if no properties were returend for a file if (isset($response['propstat']) && is_array($response['propstat'])) { foreach ($response['propstat'] as $status => $props) { echo " \n"; echo " \n"; foreach ($props as $prop) { if (!is_array($prop) || !isset($prop['name'])) { continue; } // empty properties (cannot use empty for check as '0' // is a legal value here) if (!isset($prop['value']) || empty($prop['value']) && $prop['value'] !== 0) { if ($prop['ns'] == 'DAV:') { echo " \n"; continue; } if (!empty($prop['ns'])) { echo ' <' . $response['ns_hash'][$prop['ns']] . ":$prop[name]/>\n"; continue; } echo " <$prop[name] xmlns=\"\"/>"; continue; } // some WebDAV properties need special treatment if ($prop['ns'] == 'DAV:') { switch ($prop['name']) { case 'creationdate': echo " \n"; echo ' ' . gmdate('Y-m-d\TH:i:s\Z', $prop['value']) . "\n"; echo " \n"; break; case 'getlastmodified': echo " \n"; echo ' ' . gmdate('D, d M Y H:i:s', $prop['value']) . " GMT\n"; echo " \n"; break; case 'resourcetype': echo " \n"; echo " \n"; echo " \n"; break; case 'supportedlock': if (is_array($prop[value])) { $prop[value] = $this->_lockentries($prop[value]); } echo " \n"; echo " $prop[value]\n"; echo " \n"; break; case 'lockdiscovery': if (is_array($prop[value])) { $prop[value] = $this->_activelocks($prop[value]); } echo " \n"; echo " $prop[value]\n"; echo " \n"; break; default: echo " \n"; echo ' ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n"; echo " \n"; } continue; } if ($prop['name'] == 'calendar-data' && is_object($prop['value'])) { $prop['value'] = $prop['value']->serialize(); } if (!empty($prop['ns'])) { echo ' <' . $response['ns_hash'][$prop['ns']] . ":$prop[name]>\n"; echo ' ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n"; echo ' \n"; continue; } echo " <$prop[name] xmlns=\"\">\n"; echo ' ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n"; echo " \n"; } echo " \n"; echo " HTTP/1.1 $status\n"; echo " \n"; } } if (isset($response['status'])) { echo " HTTP/1.1 $status\n"; } if (isset($response['responsedescription'])) { echo " \n"; echo ' ' . $this->_prop_encode(htmlspecialchars($response['responsedescription'])) . "\n"; echo " \n"; } echo " \n"; } echo "\n"; } } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * c-handling-comment-ender-p: nil * End: */ ?>