$lockscope = strtolower(substr($tag, 5)); } break; /* case 'DAV::READ': */ /** RFC2518 is pretty vague about read locks */ /* case 'DAV::READ': */ /** RFC2518 is pretty vague about read locks */ case 'DAV::write': dbg_error_log("LOCK", ":Request: %s -> %s", $v['type'], $tag); if ($inside['DAV::locktype'] && $v['type'] == "complete") { $locktype = strtolower(substr($tag, 5)); } break; case 'DAV::href': dbg_error_log("LOCK", ":Request: %s -> %s", $v['type'], $tag); dbg_log_array("LOCK", "DAV:href", $v, true); if ($inside['DAV::owner'] && $v['type'] == "complete") { $lockowner = $v['value']; } break; default: if (preg_match('/^(.*):([^:]+)$/', $tag, $matches)) { $unsupported[$matches[2]] = $matches[1]; } else { $unsupported[$tag] = ""; } dbg_error_log("LOCK", "Unhandled tag >>%s<<", $tag); } } $request->UnsupportedRequest($unsupported); // Won't return if there was unsupported stuff.
break; case 'MOVE': include 'caldav-MOVE.php'; break; case 'ACL': include 'caldav-ACL.php'; break; case 'LOCK': include 'caldav-LOCK.php'; break; case 'UNLOCK': include 'caldav-LOCK.php'; break; case 'MKTICKET': include 'caldav-MKTICKET.php'; break; case 'DELTICKET': include 'caldav-DELTICKET.php'; break; case 'BIND': include 'caldav-BIND.php'; break; case 'TESTRRULE': include 'test-RRULE-v2.php'; break; default: dbg_error_log('caldav', 'Unhandled request method >>%s<<', $request->method); dbg_log_array('caldav', '_SERVER', $_SERVER, true); dbg_error_log('caldav', 'RAW: %s', str_replace("\n", '', str_replace("\r", '', $request->raw_post))); } $request->DoResponse(400, translate('The application program does not understand that request.'));
*/ $searches = $xmltree->GetPath('/DAV::principal-property-search/DAV::property-search'); dbg_log_array("principal", "SEARCH", $searches, true); $clause_joiner = " AND "; $CS_search_test = $xmltree->GetAttribute('test'); if (isset($CS_search_test) && $CS_search_test == 'anyof') { $clause_joiner = " OR "; } $params = array(); $where = ""; foreach ($searches as $k => $search) { $qry_props = $search->GetPath('/DAV::property-search/DAV::prop/*'); // There may be many $match = $search->GetPath('/DAV::property-search/DAV::match'); // There may only be one dbg_log_array("principal", "MATCH", $match, true); $match = $match[0]->GetContent(); $subwhere = ""; foreach ($qry_props as $k1 => $v1) { if ($subwhere != "") { $subwhere .= " OR "; } switch ($v1->GetNSTag()) { case 'DAV::displayname': $subwhere .= ' displayname ILIKE :displayname_match '; $params[':displayname_match'] = '%' . $match . '%'; break; case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set': $match = preg_replace('{^.*/caldav.php/([^/]+)(/.*)?$}', '\\1', $match); $match = preg_replace('{^mailto:}', '', $match); $subwhere .= ' (email ILIKE :user_address_match OR username ILIKE :user_address_match) ';
$request->DoResponse(404); } if (!($target_collection->IsAddressbook() || $target_collection->IsSchedulingCollection())) { $request->DoResponse(403, translate('The addressbook-query report must be run against an addressbook collection')); } /** * @todo Once we are past DB version 1.2.1 we can change this query more radically. The best performance to * date seems to be: * SELECT caldav_data.*,address_item.* FROM collection JOIN address_item USING (collection_id,user_no) * JOIN caldav_data USING (dav_id) WHERE collection.dav_name = '/user1/home/' * AND caldav_data.caldav_type = 'VEVENT' ORDER BY caldav_data.user_no, caldav_data.dav_name; */ $params = array(); $where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id(); if (is_array($qry_filters)) { dbg_log_array('cardquery', 'qry_filters', $qry_filters, true); $components = array(); $filter_fragment = SqlFilterCardDAV($qry_filters, $components); if ($filter_fragment !== false) { $where .= ' ' . $filter_fragment['sql']; $params = $filter_fragment['params']; } } else { dbg_error_log('cardquery', 'No query filters'); } $sql = 'SELECT * FROM caldav_data INNER JOIN addressbook_resource USING(dav_id)' . $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY dav_id"; } $qry = new AwlQuery($sql, $params); if ($qry->Exec("cardquery", __LINE__, __FILE__) && $qry->rows() > 0) {
} if ($c->enable_scheduling != true) { $request->DoResponse(404, translate('The application program does not understand that request.')); // Does not return } dbg_log_array('well-known', 'method:' . $request->method); switch ($request->method) { case 'GET': ischedule_get(); break; case 'POST': include 'iSchedule-POST.php'; break; default: dbg_error_log('well-known', 'Unhandled request method >>%s<<', $request->method); dbg_log_array('well-known', '_SERVER', $_SERVER, true); dbg_error_log('well-known', 'RAW: %s', str_replace("\n", '', str_replace("\r", '', $request->raw_post))); } $request->DoResponse(500, translate('The application program does not understand that request.')); function ischedule_get() { global $request, $c; if ($request->path != '/.well-known/ischedule' || $_GET['query'] != 'capabilities') { $request->DoResponse(404, translate('The application program does not understand that request.' . $request->path)); return false; } header('iSchedule-Version: 1.0'); header('Content-Type: application/xml; charset=utf-8'); echo '<?xml version="1.0" encoding="utf-8" ?>'; echo <<<RESPONSE <query-result xmlns="urn:ietf:params:xml:ns:ischedule">
/** * Function to dump an array to the error log, possibly recursively * * @var string $component Which component should this log message identify itself from * @var string $name What name should this array dump identify itself as * @var array $arr The array to be dumped. * @var boolean $recursive Should the dump recurse into arrays/objects in the array */ function dbg_log_array($component, $name, $arr, $recursive = false) { if (!isset($arr) || gettype($arr) != 'array' && gettype($arr) != 'object') { dbg_error_log($component, "%s: array is not set, or is not an array!", $name); return; } foreach ($arr as $key => $value) { dbg_error_log($component, "%s: >>%s<< = >>%s<<", $name, $key, gettype($value) == 'array' || gettype($value) == 'object' ? gettype($value) : $value); if ($recursive && (gettype($value) == 'array' || gettype($value) == 'object' && "{$key}" != 'self' && "{$key}" != 'parent')) { dbg_log_array($component, "{$name}" . "[{$key}]", $value, $recursive); } } }
/** * Set the translation to the user's locale. At this stage all we do is * call the gettext function. */ function awl_set_locale($locale) { global $c; if (!is_array($locale) && !preg_match('/^[a-z]{2}(_[A-Z]{2})?\\./', $locale)) { $locale = array($locale, $locale . ".UTF-8"); } if ($newlocale = setlocale(LC_ALL, $locale)) { dbg_error_log("I18N", "Set locale to =%s=", $newlocale); $c->current_locale = $newlocale; } else { dbg_log_array("I18N", "Unsupported locale: ", $locale, false); } }
} } else { dbg_error_log("REPORT", "Unexpected DAV::PROP type of " . $v['type'] . " when no active report type."); } break; case 'DAV::GETETAG': case 'DAV::GETCONTENTLENGTH': case 'DAV::GETCONTENTTYPE': case 'DAV::RESOURCETYPE': if (isset($report_properties)) { $attribute = substr($v['tag'], 5); $report_properties[$attribute] = 1; } break; case 'DAV::HREF': dbg_log_array("REPORT", "DAV::HREF", $v, true); if (isset($report[$reportnum]['multiget'])) { $multiget_names[] = $v['value']; } default: dbg_error_log("REPORT", "Unhandled tag >>" . $v['tag'] . "<<"); } } if ($reportnum == -1) { // Fake the request anyway... $reportnum++; $report_type = substr($v['tag'], 30); $report[$reportnum]['type'] = $report_type; $report[$reportnum]['include_href'] = 1; $report[$reportnum]['include_data'] = 0; $report[$reportnum]['start'] = date('Ymd\\THis\\Z', time() - 86400 * 40);
} /** * We're here because they allow recursive reports, and this appears to be such a location. */ $where = 'WHERE caldav_data.collection_id IN '; $where .= '(SELECT bound_source_id FROM dav_binding WHERE dav_binding.dav_name ~ :path_match '; $where .= 'UNION '; $where .= 'SELECT collection_id FROM collection WHERE collection.dav_name ~ :path_match) '; $distinct = 'DISTINCT ON (calendar_item.uid) '; $params[':path_match'] = '^' . $target_collection->bound_from(); } else { $where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id(); $distinct = ''; } if (is_array($qry_filters)) { dbg_log_array("calquery", "qry_filters", $qry_filters, true); $components = array(); $filter_fragment = SqlFilterFragment($qry_filters, $components); if ($filter_fragment !== false) { $where .= ' ' . $filter_fragment['sql']; $params = array_merge($params, $filter_fragment['params']); } } if ($target_collection->Privileges() != privilege_to_bits('DAV::all')) { $where .= " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) "; } if (isset($c->hide_TODO) && ($c->hide_TODO === true || is_string($c->hide_TODO) && preg_match($c->hide_TODO, $_SERVER['HTTP_USER_AGENT'])) && !$target_collection->HavePrivilegeTo('all')) { $where .= " AND caldav_data.caldav_type NOT IN ('VTODO') "; } if (isset($c->hide_older_than) && intval($c->hide_older_than > 0)) { $where .= " AND (CASE WHEN caldav_data.caldav_type<>'VEVENT' OR calendar_item.dtstart IS NULL THEN true ELSE calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') END) ";
$item->addCategory(array('term' => trim($category))); } } $p_description = $event->description; if ($p_description != '') { $content .= '<br />' . '<br />' . '<strong>' . translate('Description') . '</strong>:<br />' . nl2br(hyperlink($p_description)); $item->setDescription($p_description); } $item->setContent($content); $feed->addEntry($item); //break; } $last_modified = new RepeatRuleDateTime($collection->GetProperty('modified')); $feed->setDateModified($last_modified->epoch()); $response = $feed->export('atom'); $cache->set($cache_ns, $cache_key, $response); return $response; } if ($request->method == 'GET') { $collection = new DAVResource($request->path); $response = caldav_get_feed($request, $collection); header('Content-Length: ' . strlen($response)); header('Etag: ' . $collection->unique_tag()); $request->DoResponse(200, $request->method == 'HEAD' ? '' : $response, 'text/xml; charset="utf-8"'); } else { dbg_error_log('feed', 'Unhandled request method >>%s<<', $request->method); dbg_log_array('feed', '_SERVER', $_SERVER, true); dbg_error_log('feed', 'RAW: %s', str_replace("\n", '', str_replace("\r", '', $request->raw_post))); } $request->DoResponse(500, translate('The application program does not understand that request.')); /* vim: set ts=2 sw=2 tw=0 :*/
break; case 'REPORT': include_once "caldav-REPORT.php"; break; case 'PROPFIND': include_once "caldav-PROPFIND.php"; break; case 'GET': include_once "caldav-GET.php"; break; case 'HEAD': include_once "caldav-GET.php"; break; case 'PROPPATCH': case 'MKCALENDAR': case 'MKCOL': case 'PUT': case 'DELETE': case 'LOCK': case 'UNLOCK': $request->DoResponse(403, translate('Anonymous users are not allowed to modify calendars')); break; case 'TESTRRULE': include_once "test-RRULE.php"; break; default: dbg_error_log("caldav", "Unhandled request method >>%s<<", $request->method); dbg_log_array("caldav", '_SERVER', $_SERVER, true); dbg_error_log("caldav", "RAW: %s", str_replace("\n", "", str_replace("\r", "", $request->raw_post))); } $request->DoResponse(500, translate("The application program does not understand that request."));
/** * Returns an XML sub-tree for a single collection record from the DB */ function collection_to_xml($collection) { global $arbitrary, $attribute_list, $session, $c, $request; dbg_error_log("PROPFIND", "Building XML Response for collection '%s'", $collection->dav_name); $collection->properties = get_arbitrary_properties($collection->dav_name); $url = $_SERVER['SCRIPT_NAME'] . $collection->dav_name; $resourcetypes = array(new XMLElement("collection")); $contentlength = false; if ($collection->is_calendar == 't') { $resourcetypes[] = clone new XMLElement("calendar", false, array("xmlns" => "urn:ietf:params:xml:ns:caldav")); $lqry = new PgQuery("SELECT sum(length(caldav_data)) FROM caldav_data WHERE user_no = ? AND dav_name ~ ?;", $collection->user_no, $collection->dav_name . '[^/]+$'); if ($lqry->Exec("PROPFIND", __LINE__, __FILE__) && ($row = $lqry->Fetch())) { $contentlength = $row->sum; } } if ($collection->is_principal == 't') { $resourcetypes[] = clone new XMLElement("principal"); } dbg_log_array("PROPFIND", 'attribute_list', $attribute_list, true); $prop = new XMLElement("prop"); if (isset($attribute_list['ALLPROP']) || isset($attribute_list['GETLASTMODIFIED'])) { $prop->NewElement("getlastmodified", isset($collection->modified) ? $collection->modified : false); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTLENGTH'])) { $prop->NewElement("getcontentlength", $contentlength); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTTYPE'])) { $prop->NewElement("getcontenttype", "httpd/unix-directory"); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['CREATIONDATE'])) { $prop->NewElement("creationdate", $collection->created); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['RESOURCETYPE'])) { dbg_error_log("PROPFIND", "Appending resourcetype results"); $prop->NewElement("resourcetype", $resourcetypes); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['DISPLAYNAME'])) { $displayname = $collection->dav_displayname == "" ? ucfirst(trim(str_replace("/", " ", $collection->dav_name))) : $collection->dav_displayname; $prop->NewElement("displayname", $displayname); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['GETETAG'])) { $prop->NewElement("getetag", '"' . $collection->dav_etag . '"'); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['CURRENT-USER-PRIVILEGE-SET'])) { $prop->NewElement("current-user-privilege-set", privileges($request->permissions)); } if (count($arbitrary) > 0) { foreach ($arbitrary as $k => $v) { $prop->NewElement($k, $collection->properties[$k]); } } if (isset($attribute_list['ACL'])) { /** * FIXME: This information is semantically valid but presents an incorrect picture. */ $principal = new XMLElement("principal"); $principal->NewElement("authenticated"); $grant = new XMLElement("grant", array(privileges($request->permissions))); $prop->NewElement("acl", new XMLElement("ace", array($principal, $grant))); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTLANGUAGE'])) { $contentlength = strlen($item->caldav_data); $prop->NewElement("getcontentlanguage", $c->current_locale); } if (isset($attribute_list['SUPPORTEDLOCK'])) { $prop->NewElement("supportedlock", new XMLElement("lockentry", array(new XMLElement("lockscope", new XMLElement("exclusive")), new XMLElement("locktype", new XMLElement("write"))))); } if (isset($attribute_list['ALLPROP']) || isset($attribute_list['SUPPORTED-PRIVILEGE-SET'])) { $prop->NewElement("supported-privilege-set", privileges($request->SupportedPrivileges(), "supported-privilege")); } $status = new XMLElement("status", "HTTP/1.1 200 OK"); $propstat = new XMLElement("propstat", array($prop, $status)); $href = new XMLElement("href", $url); $response = new XMLElement("response", array($href, $propstat)); return $response; }
/** * Set the translation to the user's locale. At this stage all we do is * call the gettext function. */ function awl_set_locale($locale) { global $c; if (!is_array($locale) && !preg_match('/^[a-z]{2}(_[A-Z]{2})?\\./', $locale)) { $locale = array($locale, $locale . '.UTF-8'); } if (!function_exists('setlocale')) { dbg_log_array('WARN', 'No "setlocale()" function? PHP gettext support missing?'); return; } if ($newlocale = setlocale(LC_ALL, $locale)) { dbg_error_log('I18N', 'Set locale to =%s=', $newlocale); $c->current_locale = $newlocale; } else { dbg_log_array('I18N', 'Unsupported locale: ', $locale, false); } }
if (preg_match('{^/(\\S+@[a-z0-9][a-z0-9-]*[.][a-z0-9.-]+)/?$}i', $request->path, $matches)) { $principal = new Principal('email', $matches[1]); $path_match = '^' . $principal->dav_name(); } if (isset($fb_format) && $fb_format != 'text/calendar') { $request->DoResponse(406, translate('This server only supports the text/calendar format for freebusy URLs')); } if (!$request->HavePrivilegeTo('read-free-busy')) { $request->DoResponse(404); } require_once "freebusy-functions.php"; switch ($_SERVER['REQUEST_METHOD']) { case 'GET': $range_start = new RepeatRuleDateTime($fb_start); if (!isset($fb_end)) { $range_end = clone $range_start; $range_end->modify($fb_period); } else { $range_end = new RepeatRuleDateTime($fb_end); } $freebusy = get_freebusy($path_match, $range_start, $range_end); $result = new vCalendar(); $result->AddComponent($freebusy); $request->DoResponse(200, $result->Render(), 'text/calendar'); break; default: dbg_error_log("freebusy", "Unhandled request method >>%s<<", $_SERVER['REQUEST_METHOD']); dbg_log_array("freebusy", 'HEADERS', $raw_headers); dbg_log_array("freebusy", '_SERVER', $_SERVER, true); @dbg_error_log("freebusy", "RAW: %s", str_replace("\n", "", str_replace("\r", "", $request->raw_post))); }