function fetch_db_zone($tzid) { $tzrow = null; $qry = new AwlQuery('SELECT * FROM timezones WHERE tzid=:tzid', array(':tzid' => $tzid)); if ($qry->Exec('tz/update', __LINE__, __FILE__) && $qry->rows() > 0) { $tzrow = $qry->Fetch(); } return $tzrow; }
/** * Method used to get the user's roles */ function GetRoles() { $this->roles = array(); $sql = 'SELECT role_name FROM roles JOIN role_member ON roles.role_no=role_member.role_no WHERE user_no = ' . $this->user_no; $qry = new AwlQuery($sql); if ($qry->Exec('DAViCalSession') && $qry->rows() > 0) { while ($role = $qry->Fetch()) { $this->roles[$role->role_name] = 1; } } }
function delete_collection($id) { $params = array(':collection_id' => $id); $qry = new AwlQuery('SELECT child.collection_id AS child_id FROM collection child JOIN collection parent ON (parent.dav_name = child.parent_container) WHERE parent.collection_id = :collection_id', $params); if ($qry->Exec('DELETE', __LINE__, __FILE__) && $qry->rows() > 0) { while ($row = $qry->Fetch()) { delete_collection($row->child_id); } } if ($qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE collection_id = :collection_id", $params) && $qry->QDo("DELETE FROM property WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params) && $qry->QDo("DELETE FROM locks WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params) && $qry->QDo("DELETE FROM caldav_data WHERE collection_id = :collection_id", $params) && $qry->QDo("DELETE FROM collection WHERE collection_id = :collection_id", $params)) { @dbg_error_log("DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path); return true; } return false; }
static function getInstance($name) { $qry = new AwlQuery('SELECT * FROM timezones WHERE tzid = ? ORDER BY active DESC', $name); if ($qry->Exec('VTimezone', __LINE__, __FILE__) && $qry->rows() > 0 && ($row = $qry->Fetch())) { $vtz = new vComponent($row->vtimezone); if ($vtz->GetType() == 'VTIMEZONE') { return $vtz; } $tmp = $vtz->GetComponents('VTIMEZONE'); if (count($tmp) < 1 || $tmp[0]->GetType() != 'VTIMEZONE') { return null; } $vtz = $tmp[0]; return $vtz; } return null; }
function SQLTest() { $result = ''; $sql = "SELECT event_instances::timestamp AS event_date FROM event_instances(?,?) LIMIT 30;"; $qry = new AwlQuery($sql, $this->dtstart, $this->recur); // printf( "%s\n", $qry->querystring); if ($qry->Exec("test") && $qry->rows > 0) { $i = 0; while ($row = $qry->Fetch()) { if ($i++ % 4 == 0) { $result .= "\n"; } $result .= " " . $row->event_date; } } return $result; }
function SQLTest() { $result = ''; $sql = "SELECT event_instances::timestamp AS event_date FROM event_instances(:dtstart,:rrule) LIMIT " . $this->result_limit; $start = microtime(true); $qry = new AwlQuery($sql, array(':dtstart' => $this->dtstart, ':rrule' => $this->recur)); // printf( "%s\n", $qry->querystring); if ($qry->Exec("test") && $qry->rows() > 0) { $i = 0; while ($row = $qry->Fetch()) { if ($i++ % 4 == 0) { $result .= "\n"; } $result .= " " . $row->event_date; } } $this->SQL_time = microtime(true) - $start; return $result; }
/** * Authenticate against a different PostgreSQL database which contains a usr table in * the AWL format. * * Use this as in the following example config snippet: * * require_once('auth-functions.php'); * $c->authenticate_hook = array( * 'call' => 'AuthExternalAwl', * 'config' => array( * // A PgSQL database connection string for the database containing user records * 'connection[]' => 'dbname=wrms host=otherhost port=5433 user=general', * // Which columns should be fetched from the database * 'columns' => "user_no, active, email_ok, joined, last_update AS updated, last_used, username, password, fullname, email", * // a WHERE clause to limit the records returned. * 'where' => "active AND org_code=7" * ) * ); * */ function AuthExternalAWL($username, $password) { global $c; $persistent = isset($c->authenticate_hook['config']['use_persistent']) && $c->authenticate_hook['config']['use_persistent']; if (isset($c->authenticate_hook['config']['columns'])) { $cols = $c->authenticate_hook['config']['columns']; } else { $cols = '*'; } if (isset($c->authenticate_hook['config']['where'])) { $andwhere = ' AND ' . $c->authenticate_hook['config']['where']; } else { $andwhere = ''; } $qry = new AwlQuery('SELECT ' . $cols . ' FROM usr WHERE lower(username) = :username ' . $andwhere, array(':username' => strtolower($username))); $authconn = $qry->SetConnection($c->authenticate_hook['config']['connection'], $persistent ? array(PDO::ATTR_PERSISTENT => true) : null); if (!$authconn) { echo <<<EOERRMSG <html><head><title>Database Connection Failure</title></head><body> <h1>Database Error</h1> <h3>Could not connect to PostgreSQL database</h3> </body> </html> EOERRMSG; exit(1); } if ($qry->Exec('Login', __LINE__, __FILE__) && $qry->rows() == 1) { $usr = $qry->Fetch(); if (session_validate_password($password, $usr->password)) { UpdateUserFromExternal($usr); /** * We disallow login by inactive users _after_ we have updated the local copy */ if (isset($usr->active) && $usr->active == 'f') { return false; } $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $usr->username)); if ($qry->Exec() && $qry->rows() == 1) { $principal = $qry->Fetch(); return $principal; } return $usr; // Somewhat optimistically } } return false; }
/** * Returns the locked row, either from the cache or from the database * * @param string $dav_name The resource which we want to know the lock status for */ function GetLockRow($lock_token) { if (isset($this->_locks_found) && isset($this->_locks_found[$lock_token])) { return $this->_locks_found[$lock_token]; } $qry = new AwlQuery('SELECT * FROM locks WHERE opaquelocktoken = :lock_token', array(':lock_token' => $lock_token)); if ($qry->Exec('caldav', __LINE__, __FILE__)) { $lock_row = $qry->Fetch(); $this->_locks_found = array($lock_token => $lock_row); return $this->_locks_found[$lock_token]; } else { $this->DoResponse(500, translate("Database Error")); } return false; // Nothing matched }
if (!$qry->Exec("GET", __LINE__, __FILE__)) { $request->DoResponse(500, translate("Database Error")); } /** * Here we are constructing a whole calendar response for this collection, including * the timezones that are referred to by the events we have selected. */ $vcal = new iCalComponent(); $vcal->VCalendar(); $displayname = $dav_resource->GetProperty('displayname'); if (isset($displayname)) { $vcal->AddProperty("X-WR-CALNAME", $displayname); } $need_zones = array(); $timezones = array(); while ($event = $qry->Fetch()) { $ical = new iCalComponent($event->caldav_data); /** Save the timezone component(s) into a minimal set for inclusion later */ $event_zones = $ical->GetComponents('VTIMEZONE', true); foreach ($event_zones as $k => $tz) { $tzid = $tz->GetPValue('TZID'); if (!isset($tzid)) { continue; } if ($tzid != '' && !isset($timezones[$tzid])) { $timezones[$tzid] = $tz; } } /** Work out which ones are actually used here */ $comps = $ical->GetComponents('VTIMEZONE', false); foreach ($comps as $k => $comp) {
/** * Do what must be done with time zones from on file. Attempt to turn * them into something that PostgreSQL can understand... * * @DEPRECATED: This class will be removed soon. * @todo Remove this function. */ function DealWithTimeZones() { global $c; deprecated('iCalendar::DealWithTimeZones'); $tzid = $this->Get('TZID'); if (isset($c->save_time_zone_defs) && $c->save_time_zone_defs) { $qry = new AwlQuery("SELECT tz_locn FROM time_zone WHERE tz_id = ?;", $tzid); if ($qry->Exec('iCalendar') && $qry->rows() == 1) { $row = $qry->Fetch(); $this->tz_locn = $row->tz_locn; } dbg_error_log('iCalendar', " TZCrap2: TZID '%s', DB Rows=%d, Location '%s'", $tzid, $qry->rows(), $this->tz_locn); } if ((!isset($this->tz_locn) || $this->tz_locn == '') && $tzid != '') { /** * In case there was no X-LIC-LOCATION defined, let's hope there is something in the TZID * that we can use. We are looking for a string like "Pacific/Auckland" if possible. */ $tzname = preg_replace('#^(.*[^a-z])?([a-z]+/[a-z]+)$#i', '$1', $tzid); /** * Unfortunately this kind of thing will never work well :-( * if ( strstr( $tzname, ' ' ) ) { $words = preg_split('/\s/', $tzname ); $tzabbr = ''; foreach( $words AS $i => $word ) { $tzabbr .= substr( $word, 0, 1); } $this->tz_locn = $tzabbr; } */ if (preg_match('#\\S+/\\S+#', $tzname)) { $this->tz_locn = $tzname; } dbg_error_log('iCalendar', " TZCrap3: TZID '%s', Location '%s', Perhaps: %s", $tzid, $this->tz_locn, $tzname); } if ($tzid != '' && isset($c->save_time_zone_defs) && $c->save_time_zone_defs && $qry->rows() != 1 && isset($this->vtimezone) && $this->vtimezone != "") { $qry2 = new AwlQuery("INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES( ?, ?, ? );", $tzid, $this->tz_locn, $this->vtimezone); $qry2->Exec('iCalendar'); } if ((!isset($this->tz_locn) || $this->tz_locn == "") && isset($c->local_tzid)) { $this->tz_locn = $c->local_tzid; } }
$qry->Begin(); $etag = md5($request->raw_post); $params = array(':user_no' => $dest->GetProperty('user_no'), ':dav_name' => $dest->bound_from(), ':etag' => $etag, ':dav_data' => $request->raw_post, ':session_user' => $session->user_no); if ($dest->Exists()) { $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, logged_user=:session_user, modified=current_timestamp, user_no=:user_no, caldav_type=\'VCARD\' WHERE dav_name=:dav_name'; $response_code = 200; $qry->QDo($sql, $params); $qry->QDo("SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ", array(':dav_name' => $params[':dav_name'])); } else { $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id ) VALUES( :user_no, :dav_name, :etag, :dav_data, \'VCARD\', :session_user, current_timestamp, current_timestamp, :collection_id )'; $params[':collection_id'] = $collection_id; $response_code = 201; $qry->QDo($sql, $params); $qry->QDo("SELECT currval('dav_id_seq') AS dav_id"); } $row = $qry->Fetch(); require_once 'vcard.php'; $vcard = new vCard($request->raw_post); $vcard->Write($row->dav_id, $dest->Exists()); $qry->QDo("SELECT write_sync_change( {$collection_id}, {$response_code}, :dav_name)", array(':dav_name' => $dest->bound_from())); if (!$qry->Commit()) { $qry->Rollback(); $request->DoResponse(500, "A database error occurred"); } header('ETag: "' . $etag . '"'); if ($response_code == 200) { $response_code = 204; } $request->DoResponse($response_code);
$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) { while ($address_object = $qry->Fetch()) { if (!$need_post_filter || apply_filter($qry_filters, $address_object)) { if ($bound_from != $target_collection->dav_name()) { $address_object->dav_name = str_replace($bound_from, $target_collection->dav_name(), $address_object->dav_name); } if (count($address_data_properties) > 0) { $vcard = new VCard($address_object->caldav_data); $vcard->MaskProperties($address_data_properties); $address_object->caldav_data = $vcard->Render(); } $responses[] = component_to_xml($properties, $address_object); } } } $multistatus = new XMLElement("multistatus", $responses, $reply->GetXmlNsArray()); $request->XMLResponse(207, $multistatus);
/** * Write the User record. * @return Success. */ function Write() { global $c, $session; if (parent::Write()) { $c->messages[] = i18n('User record written.'); if ($this->WriteType == 'insert') { $qry = new AwlQuery("SELECT currval('usr_user_no_seq');"); $qry->Exec("User::Write"); $sequence_value = $qry->Fetch(true); // Fetch as an array $this->user_no = $sequence_value[0]; } else { if ($this->user_no == $session->user_no && $this->Get("date_format_type") != $session->date_format_type) { // Ensure we match the date style setting $session->date_format_type = $this->Get("date_format_type"); unset($_POST['email_ok']); $qry = new AwlQuery("SET DATESTYLE TO ?;", $this->Get("date_format_type") == 'E' ? 'European,ISO' : ($this->Get("date_format_type") == 'U' ? 'US,ISO' : 'ISO')); $qry->Exec(); } } return $this->WriteRoles(); } return false; }
$dav_id = $row->collection_id; } else { create_external('/.external/' . md5($href), true, false); $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array(':dav_name' => '/.external/' . md5($href))); if ($qry->rows() != 1 || !($row = $qry->Fetch())) { $request->DoResponse(500, translate('Database Error')); } $dav_id = $row->collection_id; } $sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname, external_url, type ) VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname, :external_url, :external_type )'; $params = array(':target_id' => $dav_id, ':ticket_id' => null, ':parent_container' => $parent->dav_name(), ':session_principal' => $session->principal_id, ':dav_name' => $destination_path, ':displayname' => $segment, ':external_url' => $href, ':external_type' => 'calendar'); $qry = new AwlQuery($sql, $params); if ($qry->Exec('BIND', __LINE__, __FILE__)) { $qry = new AwlQuery('SELECT bind_id from dav_binding where dav_name = :dav_name', array(':dav_name' => $destination_path)); if (!$qry->Exec('BIND', __LINE__, __FILE__) || $qry->rows() != 1 || !($row = $qry->Fetch())) { $request->DoResponse(500, translate('Database Error')); } fetch_external($row->bind_id, ''); $request->DoResponse(201); } else { $request->DoResponse(500, translate('Database Error')); } } else { $source = new DAVResource($href); if (!$source->Exists()) { $request->PreconditionFailed(403, 'DAV::bind-source-exists', translate('The BIND Request MUST identify an existing resource.')); } if ($source->IsPrincipal() || !$source->IsCollection()) { $request->PreconditionFailed(403, 'DAV::binding-allowed', translate('DAViCal only allows BIND requests for collections at present.')); }
function build_site_statistics() { $principals = translate('No. of Principals'); $collections = translate('No. of Collections'); $resources = translate('No. of Resources'); $table = <<<EOTABLE <table class="statistics"> <tr><th>{$principals}</th><th>{$collections}</th><th>{$resources}</th></tr> <tr>%s</tr> </table> EOTABLE; if (!check_database_connection()) { return sprintf($table, '<td colspan="3">' . translate('Site Statistics require the database to be available!') . '</td>'); } $sql = 'SELECT (SELECT count(1) FROM principal) AS principals, (SELECT count(1) FROM collection) AS collections, (SELECT count(1) FROM caldav_data) AS resources'; $qry = new AwlQuery($sql); if ($qry->Exec('setup', __LINE__, __FILE__) && ($s = $qry->Fetch())) { $row = sprintf('<td align="center">%s</td><td align="center">%s</td><td align="center">%s</td>', $s->principals, $s->collections, $s->resources); return sprintf($table, $row); } return sprintf($table, '<td colspan="3">' . translate('Site Statistics require the database to be available!') . '</td>'); }
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) "; } $sql = 'SELECT ' . $distinct . ' caldav_data.*,calendar_item.* FROM collection INNER JOIN caldav_data USING(collection_id) INNER JOIN calendar_item USING(dav_id) ' . $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY caldav_data.dav_id"; } $qry = new AwlQuery($sql, $params); if ($qry->Exec("calquery", __LINE__, __FILE__) && $qry->rows() > 0) { while ($dav_object = $qry->Fetch()) { try { if (!$need_post_filter || apply_filter($qry_filters, $dav_object)) { if ($bound_from != $target_collection->dav_name()) { $dav_object->dav_name = str_replace($bound_from, $target_collection->dav_name(), $dav_object->dav_name); } if ($need_expansion) { $vResource = new vComponent($dav_object->caldav_data); $expanded = getVCalendarRange($vResource); if (!$expanded->overlaps($range_filter)) { continue; } $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end, $expand_as_floating); if ($expanded->ComponentCount() == 0) { continue; }
EOSQL; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY collection.collection_id, lower(sync_changes.dav_name), sync_changes.sync_time"; } else { $sql .= " ORDER BY collection.collection_id, sync_changes.dav_name, sync_changes.sync_time"; } } $qry = new AwlQuery($sql, $params); $last_dav_name = ''; $first_status = 0; if ($qry->Exec("REPORT", __LINE__, __FILE__)) { if ($qry->rows() > 50) { // If there are more than 50 rows to send we should not send full data in response ... $c->sync_resource_data_ok = false; } while ($object = $qry->Fetch()) { if ($request_via_binding) { $object->dav_name = str_replace($bound_from, $collection_path, $object->dav_name); } if ($object->dav_name == $last_dav_name) { /** The complex case: this is the second or subsequent for this dav_id */ if ($object->sync_status == 404) { array_pop($responses); $resultset = array(new XMLElement('href', ConstructURL($object->dav_name)), new XMLElement('status', display_status($object->sync_status))); $responses[] = new XMLElement('response', $resultset); $first_status = 404; } else { if ($object->sync_status == 201 && $first_status == 404) { // ... Delete ... Create ... is indicated as a create, but don't forget we started with a delete array_pop($responses); $dav_resource = new DAVResource($object);
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 && !$target_collection->HavePrivilegeTo('DAV::write-content')) { $where .= " AND caldav_data.caldav_type NOT IN ('VTODO') "; } if (isset($c->hide_older_than) && intval($c->hide_older_than > 0)) { $where .= " AND calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') "; } $sql = 'SELECT caldav_data.*,calendar_item.* FROM collection INNER JOIN caldav_data USING(collection_id) INNER JOIN calendar_item USING(dav_id) ' . $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY caldav_data.dav_id"; } $qry = new AwlQuery($sql, $params); if ($qry->Exec("calquery", __LINE__, __FILE__) && $qry->rows() > 0) { while ($calendar_object = $qry->Fetch()) { if (!$need_post_filter || apply_filter($qry_filters, $calendar_object)) { if ($bound_from != $target_collection->dav_name()) { $calendar_object->dav_name = str_replace($bound_from, $target_collection->dav_name(), $calendar_object->dav_name); } if ($need_expansion) { $vResource = new vComponent($calendar_object->caldav_data); $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end); if ($expanded->ComponentCount() == 0) { continue; } $calendar_object->caldav_data = $expanded->Render(); } $responses[] = calendar_to_xml($properties, $calendar_object); } }
/** * sync LDAP against the DB */ function sync_LDAP() { global $c; $ldapDriver = getStaticLdap(); if ($ldapDriver->valid) { $mapping = $c->authenticate_hook['config']['mapping_field']; $attributes = array_values($mapping); $ldap_users_tmp = $ldapDriver->getAllUsers($attributes); if (sizeof($ldap_users_tmp) == 0) { return; } foreach ($ldap_users_tmp as $key => $ldap_user) { $ldap_users_info[$ldap_user[$mapping["username"]]] = $ldap_user; unset($ldap_users_tmp[$key]); } $qry = new AwlQuery("SELECT username, user_no, modified as updated FROM dav_principal where type_id=1"); $qry->Exec('sync_LDAP', __LINE__, __FILE__); while ($db_user = $qry->Fetch()) { $db_users[] = $db_user->username; $db_users_info[$db_user->username] = array('user_no' => $db_user->user_no, 'updated' => $db_user->updated); } $ldap_users = array_keys($ldap_users_info); // users only in ldap $users_to_create = array_diff($ldap_users, $db_users); // users only in db $users_to_deactivate = array_diff($db_users, $ldap_users); // users present in ldap and in the db $users_to_update = array_intersect($db_users, $ldap_users); // creation of all users; if (sizeof($users_to_create)) { $c->messages[] = sprintf(i18n('- creating record for users : %s'), join(', ', $users_to_create)); foreach ($users_to_create as $username) { $user = (object) array('user_no' => 0, 'username' => $username); $valid = $ldap_users_info[$username]; $ldap_timestamp = $valid[$mapping["updated"]]; /** * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S */ foreach ($c->authenticate_hook['config']['format_updated'] as $k => $v) { ${$k} = substr($ldap_timestamp, $v[0], $v[1]); } $ldap_timestamp = "{$Y}" . "{$m}" . "{$d}" . "{$H}" . "{$M}" . "{$S}"; $valid[$mapping["updated"]] = "{$Y}-{$m}-{$d} {$H}:{$M}:{$S}"; sync_user_from_LDAP($user, $mapping, $valid); } } // deactivating all users $params = array(); $i = 0; foreach ($users_to_deactivate as $v) { if (isset($c->do_not_sync_from_ldap) && isset($c->do_not_sync_from_ldap[$v])) { continue; } $params[':u' . $i++] = strtolower($v); } if (count($params) > 0) { $c->messages[] = sprintf(i18n('- deactivating users : %s'), join(', ', $users_to_deactivate)); $qry = new AwlQuery('UPDATE usr SET active = FALSE WHERE lower(username) IN (' . implode(',', array_keys($params)) . ')', $params); $qry->Exec('sync_LDAP', __LINE__, __FILE__); } // updating all users if (sizeof($users_to_update)) { foreach ($users_to_update as $key => $username) { $valid = $ldap_users_info[$username]; $ldap_timestamp = $valid[$mapping["updated"]]; $valid["user_no"] = $db_users_info[$username]["user_no"]; $mapping["user_no"] = "user_no"; /** * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S */ foreach ($c->authenticate_hook['config']['format_updated'] as $k => $v) { ${$k} = substr($ldap_timestamp, $v[0], $v[1]); } $ldap_timestamp = "{$Y}" . "{$m}" . "{$d}" . "{$H}" . "{$M}" . "{$S}"; $valid[$mapping["updated"]] = "{$Y}-{$m}-{$d} {$H}:{$M}:{$S}"; $db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '', ' ' => '', '-' => '')), 0, 14); if ($ldap_timestamp > $db_timestamp) { sync_user_from_LDAP($usr, $mapping, $valid); } else { unset($users_to_update[$key]); $users_nothing_done[] = $username; } } if (sizeof($users_to_update)) { $c->messages[] = sprintf(i18n('- updating user records : %s'), join(', ', $users_to_update)); } if (sizeof($users_nothing_done)) { $c->messages[] = sprintf(i18n('- nothing done on : %s'), join(', ', $users_nothing_done)); } } $admins = 0; $qry = new AwlQuery("select count(*) as admins from usr join role_member using ( user_no ) join roles using (role_no) where usr.active = true and role_name='Admin'"); $qry->Exec('sync_LDAP', __LINE__, __FILE__); while ($db_user = $qry->Fetch()) { $admins = $db_user->admins; } if ($admins == 0) { $c->messages[] = sprintf(i18n('Warning: there are no active admin users, you should fix this before logging out.')); } } }
/** * * Log the action * * @param string $action_type INSERT / UPDATE or DELETE * * @param string $uid The UID of the modified item * * @param integer $user_no The user owning the containing collection. * * @param integer $collection_id The ID of the containing collection. * * @param string $dav_name The DAV path of the item, relative to the DAViCal base path * */ function log_caldav_action($action_type, $uid, $user_no, $collection_id, $dav_name) { global $c; $t = new xmpp(); $t->tls = 'none'; $t->idle = false; if (1 == $c->dbg["ALL"] || 1 == $c->dbg["push"]) { $t->debug = true; } else { $t->debug = false; } // for now use a flat node tree layout $t->pubsubLayout = 'flat'; // get the principal_id for this collection, that's what the client will be looking for $qry = new AwlQuery('SELECT principal_id FROM principal JOIN collection USING (user_no) WHERE collection_id= :collection_id', array(':collection_id' => $collection_id)); $qry->Exec('pubsub'); $row = $qry->Fetch(); $t->open($c->notifications_server['jid'], $c->notifications_server['password']); if (isset($c->notifications_server['debug_jid'])) { $t->sendMessage($c->notifications_server['debug_jid'], "ACTION: {$action_type}\nUSER: {$user_no}\nDAV NAME: {$dav_name}\nPRINCIPAL ID: " . $row->principal_id); } $t->pubsubCreate('', 'set', '/davical-' . $row->principal_id, '<x xmlns="jabber:x:data" type="submit"><field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var="pubsub#access_model"><value>open</value></field><field var=\'pubsub#type\'>plist-apple<value></value></field></x>'); $t->pubsubPublish('', 'set', '/davical-' . $row->principal_id, '<item xmlns="plist-apple" id="' . $uid . ' " ><plistfrag xmlns="plist-apple"><key>davical</key><string>' . $uid . '</string></plistfrag></item>', $uid); $t->close(); }
function get_freebusy($path_match, $range_start, $range_end, $bin_privs = null) { global $request, $c; $debugging = false; // if ( $debugging ) { // printf( "Path: %s\n", $path_match ); // print_r( $range_start ); // print_r( $range_end ); // } if (!isset($bin_privs)) { $bin_privs = $request->Privileges(); } if (!isset($range_start) || !isset($range_end)) { $request->DoResponse(400, 'All valid freebusy requests MUST contain a time-range filter'); } $params = array(':path_match' => $path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC()); $where = ' WHERE caldav_data.dav_name ~ :path_match '; $where .= 'AND rrule_event_overlaps( dtstart, dtend, rrule, :start, :end) '; $where .= "AND caldav_data.caldav_type IN ( 'VEVENT', 'VTODO' ) "; $where .= "AND (calendar_item.transp != 'TRANSPARENT' OR calendar_item.transp IS NULL) "; $where .= "AND (calendar_item.status != 'CANCELLED' OR calendar_item.status IS NULL) "; $where .= "AND collection.is_calendar AND collection.schedule_transp = 'opaque' "; if ($bin_privs != privilege_to_bits('all')) { $where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) "; } $fbtimes = array(); $sql = 'SELECT caldav_data.caldav_data, calendar_item.rrule, calendar_item.transp, calendar_item.status, '; $sql .= "to_char(calendar_item.dtstart at time zone 'GMT'," . AWLDatabase::SqlUTCFormat . ') AS start, '; $sql .= "to_char(calendar_item.dtend at time zone 'GMT'," . AWLDatabase::SqlUTCFormat . ') AS finish, '; $sql .= "calendar_item.class, calendar_item.dav_id "; $sql .= 'FROM caldav_data INNER JOIN calendar_item USING(dav_id,user_no,dav_name,collection_id) '; $sql .= 'INNER JOIN collection USING(collection_id)'; $sql .= $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= ' ORDER BY dav_id'; } $qry = new AwlQuery($sql, $params); if ($qry->Exec("REPORT", __LINE__, __FILE__) && $qry->rows() > 0) { while ($calendar_object = $qry->Fetch()) { $extra = ''; if ($calendar_object->status == 'TENTATIVE') { $extra = ';BUSY-TENTATIVE'; } else { if (isset($c->_workaround_client_freebusy_bug) && $c->_workaround_client_freebusy_bug) { $extra = ';BUSY'; } } // if ( $debugging ) { // $extra = ';'.$calendar_object->dav_id; // } // dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s, %s", $calendar_object->start, $calendar_object->finish, $calendar_object->class ); $ics = new vComponent($calendar_object->caldav_data); $expanded = expand_event_instances($ics, $range_start, $range_end); $expansion = $expanded->GetComponents(array('VEVENT' => true, 'VTODO' => true, 'VJOURNAL' => true)); // if ( $debugging ) echo "=================== $calendar_object->dav_id ========================\n"; $dtstart_type = 'DTSTART'; foreach ($expansion as $k => $v) { // if ( $debugging ) print $k."\n".$v->Render(); $start_date = $v->GetProperty($dtstart_type); if (!isset($start_date) && $v->GetType() != 'VTODO') { $dtstart_type = 'DUE'; $start_date = $v->GetProperty($dtstart_type); } $start_date = new RepeatRuleDateTime($start_date); $duration = $v->GetProperty('DURATION'); $duration = !isset($duration) ? 'P1D' : $duration->Value(); $end_date = clone $start_date; $end_date->modify($duration); if ($end_date == $start_date || $end_date < $range_start || $start_date > $range_end) { // if ( $debugging ) // echo "-----------------------------------------------------\n"; continue; } // if ( $debugging ) // echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++\n"; $thisfb = $start_date->UTC() . '/' . $end_date->UTC() . $extra; array_push($fbtimes, $thisfb); } } } $freebusy = new vComponent(); $freebusy->setType('VFREEBUSY'); $freebusy->AddProperty('DTSTAMP', date('Ymd\\THis\\Z')); $freebusy->AddProperty('DTSTART', $range_start->UTC()); $freebusy->AddProperty('DTEND', $range_end->UTC()); sort($fbtimes); foreach ($fbtimes as $k => $v) { $text = explode(';', $v, 2); $freebusy->AddProperty('FREEBUSY', $text[0], isset($text[1]) ? array('FBTYPE' => $text[1]) : null); } return $freebusy; }
/** * sync LDAP against the DB */ function sync_LDAP() { global $c; $ldapDriver = getStaticLdap(); if (!$ldapDriver->valid) { return; } $mapping = $c->authenticate_hook['config']['mapping_field']; $attributes = array_values_mapping($mapping); $ldap_users_tmp = $ldapDriver->getAllUsers($attributes); if (sizeof($ldap_users_tmp) == 0) { return; } foreach ($ldap_users_tmp as $key => $ldap_user) { $ldap_users_info[$ldap_user[$mapping['username']]] = $ldap_user; unset($ldap_users_tmp[$key]); } $qry = new AwlQuery("SELECT username, user_no, modified as updated FROM dav_principal where type_id=1"); $qry->Exec('sync_LDAP', __LINE__, __FILE__); while ($db_user = $qry->Fetch()) { $db_users[] = $db_user->username; $db_users_info[$db_user->username] = array('user_no' => $db_user->user_no, 'updated' => $db_user->updated); } // all users from ldap $ldap_users = array_keys($ldap_users_info); // users only in ldap $users_to_create = array_diff($ldap_users, $db_users); // users only in db $users_to_deactivate = array_diff($db_users, $ldap_users); // users present in ldap and in the db $users_to_update = array_intersect($db_users, $ldap_users); // creation of all users; if (sizeof($users_to_create)) { $c->messages[] = sprintf(i18n('- creating record for users : %s'), join(', ', $users_to_create)); foreach ($users_to_create as $username) { $principal = new Principal('username', $username); $valid = $ldap_users_info[$username]; $ldap_timestamp = $valid[$mapping['modified']]; if (!empty($c->authenticate_hook['config']['format_updated'])) { /** * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S */ foreach ($c->authenticate_hook['config']['format_updated'] as $k => $v) { ${$k} = substr($ldap_timestamp, $v[0], $v[1]); } $ldap_timestamp = $Y . $m . $d . $H . $M . $S; } else { if (preg_match('{^(\\d{8})(\\d{6})(Z)?$', $ldap_timestamp, $matches)) { $ldap_timestamp = $matches[1] . 'T' . $matches[2] . $matches[3]; } else { if (empty($ldap_timestamp)) { $ldap_timestamp = date('c'); } } } $valid[$mapping['modified']] = $ldap_timestamp; sync_user_from_LDAP($principal, $mapping, $valid); } } // deactivating all users $params = array(); $i = 0; $paramstring = ''; foreach ($users_to_deactivate as $v) { if (isset($c->do_not_sync_from_ldap) && isset($c->do_not_sync_from_ldap[$v])) { continue; } if ($i > 0) { $paramstring .= ','; } $paramstring .= ':u' . $i . '::text'; $params[':u' . $i++] = strtolower($v); } if (count($params) > 0) { $c->messages[] = sprintf(i18n('- deactivating users : %s'), join(', ', $users_to_deactivate)); $qry = new AwlQuery('UPDATE usr SET active = FALSE WHERE lower(username) IN (' . $paramstring . ')', $params); $qry->Exec('sync_LDAP', __LINE__, __FILE__); Principal::cacheFlush('lower(username) IN (' . $paramstring . ')', $params); } // updating all users if (sizeof($users_to_update)) { foreach ($users_to_update as $key => $username) { $principal = new Principal('username', $username); $valid = $ldap_users_info[$username]; $ldap_timestamp = $valid[$mapping['modified']]; $valid['user_no'] = $db_users_info[$username]['user_no']; $mapping['user_no'] = 'user_no'; /** * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S */ foreach ($c->authenticate_hook['config']['format_updated'] as $k => $v) { ${$k} = substr($ldap_timestamp, $v[0], $v[1]); } $ldap_timestamp = $Y . $m . $d . $H . $M . $S; $valid[$mapping['modified']] = "{$Y}-{$m}-{$d} {$H}:{$M}:{$S}"; $db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '', ' ' => '', '-' => '')), 0, 14); if ($ldap_timestamp > $db_timestamp) { sync_user_from_LDAP($principal, $mapping, $valid); } else { unset($users_to_update[$key]); $users_nothing_done[] = $username; } } if (sizeof($users_to_update)) { $c->messages[] = sprintf(i18n('- updating user records : %s'), join(', ', $users_to_update)); } if (sizeof($users_nothing_done)) { $c->messages[] = sprintf(i18n('- nothing done on : %s'), join(', ', $users_nothing_done)); } } $admins = 0; $qry = new AwlQuery("SELECT count(*) AS admins FROM usr JOIN role_member USING ( user_no ) JOIN roles USING (role_no) WHERE usr.active=TRUE AND role_name='Admin'"); $qry->Exec('sync_LDAP', __LINE__, __FILE__); while ($db_user = $qry->Fetch()) { $admins = $db_user->admins; } if ($admins == 0) { $c->messages[] = sprintf(i18n('Warning: there are no active admin users! You should fix this before logging out. Consider using the $c->do_not_sync_from_ldap configuration setting.')); } }
function ticket_row_editor() { global $c, $id, $editor, $can_write_principal, $privilege_names; $ticketrow = new Editor("Tickets", "access_ticket"); $ticketrow->SetSubmitName('ticketrow'); if ($can_write_principal && $ticketrow->IsSubmit()) { $username = $editor->Value('username'); $ugly_path = $_POST['target']; if ($ugly_path == '/' . $username || $ugly_path == '/' . $username . '/') { $target_collection = $id; } else { $username_len = strlen($username) + 2; $sql = "SELECT collection_id FROM collection WHERE dav_name = :exact_name"; $sql .= " AND substring(dav_name FROM 1 FOR {$username_len}) = '/{$username}/'"; $params = array(':exact_name' => $ugly_path); if (!preg_match('#/$#', $ugly_path)) { $sql .= " OR dav_name = :truncated_name OR dav_name = :trailing_slash_name"; $params[':truncated_name'] = preg_replace('#[^/]*$#', '', $ugly_path); $params[':trailing_slash_name'] = $ugly_path . "/"; } $sql .= " ORDER BY LENGTH(dav_name) DESC LIMIT 1"; $qry = new AwlQuery($sql, $params); if ($qry->Exec() && $qry->rows() > 0) { $row = $qry->Fetch(); $target_collection = $row->collection_id; } else { $c->messages[] = translate('Can only add tickets for existing collection paths which you own'); return $ticketrow; } } $_POST['dav_owner_id'] = $id; $_POST['target_collection_id'] = $target_collection; $ticket_id = check_by_regex($_POST['ticket_id'], '/[A-Za-z0-9]+/'); $ticketrow->SetWhere('dav_owner_id=' . $id . ' AND ticket_id=' . AwlQuery::quote($ticket_id)); if (isset($_POST['ticket_privileges'])) { $privilege_bitpos = array_flip($privilege_names); $priv_names = array_keys($_POST['ticket_privileges']); $privs_dec = privilege_to_bits($priv_names); $_POST['privileges'] = sprintf('%024s', decbin($privs_dec)); $ticketrow->Assign('privileges', $privs_dec); } $c->messages[] = translate('Creating new ticket granting privileges to this Principal'); $ticketrow->Write(); } return $ticketrow; }
function export_iCalendar(DAVResource $dav_resource) { global $session, $c, $request; if (!$dav_resource->IsCalendar() && !(isset($c->get_includes_subcollections) && $c->get_includes_subcollections)) { /** RFC2616 says we must send an Allow header if we send a 405 */ header("Allow: PROPFIND,PROPPATCH,OPTIONS,MKCOL,REPORT,DELETE"); $request->DoResponse(405, translate("GET requests on collections are only supported for calendars.")); } /** * The CalDAV specification does not define GET on a collection, but typically this is * used as a .ics download for the whole collection, which is what we do also. */ if (isset($c->get_includes_subcollections) && $c->get_includes_subcollections) { $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) '; $params = array(':path_match' => '^' . $dav_resource->dav_name()); $distinct = 'DISTINCT ON (calendar_item.uid) '; } else { $where = 'caldav_data.collection_id = :collection_id '; $params = array(':collection_id' => $dav_resource->resource_id()); $distinct = ''; } $sql = 'SELECT ' . $distinct . ' caldav_data, class, caldav_type, calendar_item.user_no, logged_user '; $sql .= 'FROM collection INNER JOIN caldav_data USING(collection_id) '; $sql .= 'INNER JOIN calendar_item USING ( dav_id ) WHERE ' . $where; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= ' ORDER BY calendar_item.uid, calendar_item.dav_id'; } $qry = new AwlQuery($sql, $params); if (!$qry->Exec("GET", __LINE__, __FILE__)) { $request->DoResponse(500, translate("Database Error")); } /** * Here we are constructing a whole calendar response for this collection, including * the timezones that are referred to by the events we have selected. */ $vcal = new iCalComponent(); $vcal->VCalendar(); $displayname = $dav_resource->GetProperty('displayname'); if (isset($displayname)) { $vcal->AddProperty("X-WR-CALNAME", $displayname); } if (!empty($c->auto_refresh_duration)) { $vcal->AddProperty("X-APPLE-AUTO-REFRESH-INTERVAL", $c->auto_refresh_duration); $vcal->AddProperty("AUTO-REFRESH", $c->auto_refresh_duration); $vcal->AddProperty("X-PUBLISHED-TTL", $c->auto_refresh_duration); } $need_zones = array(); $timezones = array(); while ($event = $qry->Fetch()) { $ical = new iCalComponent($event->caldav_data); /** Save the timezone component(s) into a minimal set for inclusion later */ $event_zones = $ical->GetComponents('VTIMEZONE', true); foreach ($event_zones as $k => $tz) { $tzid = $tz->GetPValue('TZID'); if (!isset($tzid)) { continue; } if ($tzid != '' && !isset($timezones[$tzid])) { $timezones[$tzid] = $tz; } } /** Work out which ones are actually used here */ $comps = $ical->GetComponents('VTIMEZONE', false); foreach ($comps as $k => $comp) { $tzid = $comp->GetPParamValue('DTSTART', 'TZID'); if (isset($tzid) && !isset($need_zones[$tzid])) { $need_zones[$tzid] = 1; } $tzid = $comp->GetPParamValue('DUE', 'TZID'); if (isset($tzid) && !isset($need_zones[$tzid])) { $need_zones[$tzid] = 1; } $tzid = $comp->GetPParamValue('DTEND', 'TZID'); if (isset($tzid) && !isset($need_zones[$tzid])) { $need_zones[$tzid] = 1; } if ($dav_resource->HavePrivilegeTo('all', false) || $session->user_no == $event->user_no || $session->user_no == $event->logged_user || isset($session->email) && $c->allow_get_email_visibility && $comp->IsAttendee($session->email)) { /** * These people get to see all of the event, and they should always * get any alarms as well. */ $vcal->AddComponent($comp); continue; } /** No visibility even of the existence of these events if they aren't admin/owner/attendee */ if ($event->class == 'PRIVATE') { continue; } if (!$dav_resource->HavePrivilegeTo('DAV::read') || $event->class == 'CONFIDENTIAL') { $vcal->AddComponent(obfuscated_event($comp)); } elseif (isset($c->hide_alarm) && $c->hide_alarm) { // Otherwise we hide the alarms (if configured to) $comp->ClearComponents('VALARM'); $vcal->AddComponent($comp); } else { $vcal->AddComponent($comp); } } } /** Put the timezones on there that we need */ foreach ($need_zones as $tzid => $v) { if (isset($timezones[$tzid])) { $vcal->AddComponent($timezones[$tzid]); } } return $vcal->Render(); }
/** * Writes the data to a member in the collection and returns the segment_name of the resource in our internal namespace. * @param $data iCalendar The resource to be written. * @param $create_resource boolean True if this is a new resource. * @param $segment_name The name of the resource within the collection. */ function WriteCalendarMember($data, $create_resource, $segment_name = null) { if (!$this->IsSchedulingCollection() && !$this->IsCalendar()) { return false; } // function write_resource( $user_no, $path, $caldav_data, $collection_id, $author, $etag, $ic, $put_action_type, $caldav_context, $log_action=true, $weak_etag=null ) { global $tz_regex; $resources = $ic->GetComponents('VTIMEZONE', false); // Not matching VTIMEZONE if (!isset($resources[0])) { $resource_type = 'Unknown'; /** @TODO: Handle writing non-calendar resources, like address book entries or random file data */ rollback_on_error($caldav_context, $user_no, $path, translate('No calendar content'), 412); return false; } else { $first = $resources[0]; $resource_type = $first->GetType(); } $qry = new AwlQuery(); $qry->Begin(); $params = array(':dav_name' => $path, ':user_no' => $user_no, ':etag' => $etag, ':dav_data' => $caldav_data, ':caldav_type' => $resource_type, ':session_user' => $author, ':weak_etag' => $weak_etag); if ($put_action_type == 'INSERT') { create_scheduling_requests($vcal); $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag ) VALUES( :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, current_timestamp, current_timestamp, :collection_id, :weak_etag )'; $params[':collection_id'] = $collection_id; } else { update_scheduling_requests($vcal); $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user, modified=current_timestamp, weak_etag=:weak_etag WHERE user_no=:user_no AND dav_name=:dav_name'; } if (!$qry->QDo($sql, $params)) { rollback_on_error($caldav_context, $user_no, $path); return false; } $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path)); if ($qry->rows() == 1 && ($row = $qry->Fetch())) { $dav_id = $row->dav_id; } $calitem_params = array(':dav_name' => $path, ':user_no' => $user_no, ':etag' => $etag); $dtstart = $first->GetPValue('DTSTART'); $calitem_params[':dtstart'] = $dtstart; if ((!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '') { $dtstart = $first->GetPValue('DUE'); } $dtend = $first->GetPValue('DTEND'); if (isset($dtend) && $dtend != '') { dbg_error_log('PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION')); $calitem_params[':dtend'] = $dtend; $dtend = ':dtend'; } else { $dtend = 'NULL'; if ($first->GetPValue('DURATION') != '' and $dtstart != '') { $duration = preg_replace('#[PT]#', ' ', $first->GetPValue('DURATION')); $dtend = '(:dtstart::timestamp with time zone + :duration::interval)'; $calitem_params[':duration'] = $duration; } elseif ($first->GetType() == 'VEVENT') { /** * From RFC2445 4.6.1: * For cases where a "VEVENT" calendar component specifies a "DTSTART" * property with a DATE data type but no "DTEND" property, the events * non-inclusive end is the end of the calendar date specified by the * "DTSTART" property. For cases where a "VEVENT" calendar component specifies * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property, * the event ends on the same calendar date and time of day specified by the * "DTSTART" property. * * So we're looking for 'VALUE=DATE', to identify the duration, effectively. * */ $value_type = $first->GetPParamValue('DTSTART', 'VALUE'); dbg_error_log('PUT', 'DTSTART without DTEND. DTSTART value type is %s', $value_type); if (isset($value_type) && $value_type == 'DATE') { $dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)'; } else { $dtend = ':dtstart'; } } } $last_modified = $first->GetPValue('LAST-MODIFIED'); if (!isset($last_modified) || $last_modified == '') { $last_modified = gmdate('Ymd\\THis\\Z'); } $calitem_params[':modified'] = $last_modified; $dtstamp = $first->GetPValue('DTSTAMP'); if (!isset($dtstamp) || $dtstamp == '') { $dtstamp = $last_modified; } $calitem_params[':dtstamp'] = $dtstamp; $class = $first->GetPValue('CLASS'); /* Check and see if we should over ride the class. */ /** @TODO: is there some way we can move this out of this function? Or at least get rid of the need for the SQL query here. */ if (public_events_only($user_no, $path)) { $class = 'PUBLIC'; } /* * It seems that some calendar clients don't set a class... * RFC2445, 4.8.1.3: * Default is PUBLIC */ if (!isset($class) || $class == '') { $class = 'PUBLIC'; } $calitem_params[':class'] = $class; /** Calculate what timezone to set, first, if possible */ $last_tz_locn = 'Turkmenikikamukau'; // I really hope this location doesn't exist! $tzid = $first->GetPParamValue('DTSTART', 'TZID'); if (!isset($tzid) || $tzid == '') { $tzid = $first->GetPParamValue('DUE', 'TZID'); } $timezones = $ic->GetComponents('VTIMEZONE'); foreach ($timezones as $k => $tz) { if ($tz->GetPValue('TZID') != $tzid) { /** * We'll pretend they didn't forget to give us a TZID and that they * really hope the server is running in the timezone they supplied... but be noisy about it. */ dbg_error_log('ERROR', ' Event includes TZID[%s] but uses TZID[%s]!', $tz->GetPValue('TZID'), $tzid); $tzid = $tz->GetPValue('TZID'); } // This is the one $tz_locn = $tz->GetPValue('X-LIC-LOCATION'); if (!isset($tz_locn)) { if (preg_match('#([^/]+/[^/]+)$#', $tzid, $matches)) { $tz_locn = $matches[1]; } else { if (isset($tzid) && $tzid != '') { dbg_error_log('ERROR', ' Couldn\'t guess Olsen TZ from TZID[%s]. This may end in tears...', $tzid); } } } else { if (!preg_match($tz_regex, $tz_locn)) { if (preg_match('#([^/]+/[^/]+)$#', $tzid, $matches)) { $tz_locn = $matches[1]; } } } dbg_error_log('PUT', ' Using TZID[%s] and location of [%s]', $tzid, isset($tz_locn) ? $tz_locn : ''); if (isset($tz_locn) && $tz_locn != $last_tz_locn && preg_match($tz_regex, $tz_locn)) { dbg_error_log('PUT', ' Setting timezone to %s', $tz_locn); if ($tz_locn != '') { $qry->QDo('SET TIMEZONE TO \'' . $tz_locn . "'"); } $last_tz_locn = $tz_locn; } $params = array(':tzid' => $tzid); $qry = new AwlQuery('SELECT tz_locn FROM time_zone WHERE tz_id = :tzid', $params); if ($qry->Exec('PUT', __LINE__, __FILE__) && $qry->rows() == 0) { $params[':tzlocn'] = $tz_locn; $params[':tzspec'] = isset($tz) ? $tz->Render() : null; $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params); } if (!isset($tz_locn) || $tz_locn == '') { $tz_locn = $tzid; } } $created = $first->GetPValue('CREATED'); if ($created == '00001231T000000Z') { $created = '20001231T000000Z'; } $calitem_params[':created'] = $created; $calitem_params[':tzid'] = $tzid; $calitem_params[':uid'] = $first->GetPValue('UID'); $calitem_params[':summary'] = $first->GetPValue('SUMMARY'); $calitem_params[':location'] = $first->GetPValue('LOCATION'); $calitem_params[':transp'] = $first->GetPValue('TRANSP'); $calitem_params[':description'] = $first->GetPValue('DESCRIPTION'); $calitem_params[':rrule'] = $first->GetPValue('RRULE'); $calitem_params[':url'] = $first->GetPValue('URL'); $calitem_params[':priority'] = $first->GetPValue('PRIORITY'); $calitem_params[':due'] = $first->GetPValue('DUE'); $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE'); $calitem_params[':status'] = $first->GetPValue('STATUS'); if ($put_action_type == 'INSERT') { $sql = <<<EOSQL INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp, description, rrule, tz_id, last_modified, url, priority, created, due, percent_complete, status, collection_id ) VALUES ( :user_no, :dav_name, currval('dav_id_seq'), :etag, :uid, :dtstamp, :dtstart, {$dtend}, :summary, :location, :class, :transp, :description, :rrule, :tzid, :modified, :url, :priority, :created, :due, :percent_complete, :status, {$collection_id} ) EOSQL; $sync_change = 201; } else { $sql = <<<EOSQL UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp, dtstart=:dtstart, dtend={$dtend}, summary=:summary, location=:location, class=:class, transp=:transp, description=:description, rrule=:rrule, tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority, created=:created, due=:due, percent_complete=:percent_complete, status=:status WHERE user_no=:user_no AND dav_name=:dav_name EOSQL; $sync_change = 200; } write_alarms($dav_id, $first); write_attendees($dav_id, $first); if ($log_action && function_exists('log_caldav_action')) { log_caldav_action($put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path); } else { if ($log_action) { dbg_error_log('PUT', 'No log_caldav_action( %s, %s, %s, %s, %s) can be called.', $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path); } } $qry = new AwlQuery($sql, $calitem_params); if (!$qry->Exec('PUT', __LINE__, __FILE__)) { rollback_on_error($caldav_context, $user_no, $path); return false; } $qry->QDo("SELECT write_sync_change( {$collection_id}, {$sync_change}, :dav_name)", array(':dav_name' => $path)); $qry->Commit(); dbg_error_log('PUT', 'User: %d, ETag: %s, Path: %s', $author, $etag, $path); return $segment_name; }
/** * Get the calendar_free_busy_set, as lazily as possible */ function calendar_free_busy_set() { if (!isset($this->calendar_free_busy_set)) { /** * calendar-free-busy-set has been dropped from draft 5 of the scheduling extensions for CalDAV * in favour of */ $this->calendar_free_busy_set = array(); $qry = new AwlQuery('SELECT dav_name FROM collection WHERE user_no = :user_no AND is_calendar AND (schedule_transp = \'opaque\' OR schedule_transp IS NULL) ORDER BY user_no, collection_id', array(':user_no' => $this->user_no)); if ($qry->Exec('principal', __LINE__, __FILE__)) { while ($calendar = $qry->Fetch()) { $this->calendar_free_busy_set[] = ConstructURL($calendar->dav_name, true); } } } return $this->calendar_free_busy_set; }
/** * Get XML response for items in the collection * If '/' is requested, a list of visible users is given, otherwise * a list of calendars for the user which are parented by this path. */ function get_collection_contents($depth, $collection, $parent_path = null) { global $c, $session, $request, $reply, $property_list; $bound_from = $collection->bound_from(); $bound_to = $collection->dav_name(); if (!isset($parent_path)) { $parent_path = $collection->dav_name(); } dbg_error_log('PROPFIND', 'Getting collection contents: Depth %d, Path: %s, Bound from: %s, Bound to: %s', $depth, $collection->dav_name(), $bound_from, $bound_to); $date_format = AwlDatabase::HttpDateFormat; $responses = array(); if (!$collection->IsCalendar() && !$collection->IsAddressbook()) { /** * Calendar/Addressbook collections may not contain collections, so we are only looking in the other ones */ $params = array(':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth); if ($bound_from == '/') { $sql = "SELECT usr.*, '/' || username || '/' AS dav_name, md5(username || updated::text) AS dav_etag, "; $sql .= "to_char(joined at time zone 'GMT',{$date_format}) AS created, "; $sql .= "to_char(updated at time zone 'GMT',{$date_format}) AS modified, "; $sql .= 'FALSE AS is_calendar, TRUE AS is_principal, FALSE AS is_addressbook, \'principal\' AS type, '; $sql .= 'principal_id AS collection_id, '; $sql .= 'principal.* '; $sql .= 'FROM usr JOIN principal USING (user_no) '; $sql .= "WHERE (pprivs(:session_principal::int8,principal.principal_id,:scan_depth::int) & 1::BIT(24))::INT4::BOOLEAN "; $sql .= 'ORDER BY usr.user_no'; } else { $qry = new AwlQuery('SELECT * FROM dav_binding WHERE dav_binding.parent_container = :this_dav_name ORDER BY bind_id', array(':this_dav_name' => $bound_from)); if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) { while ($binding = $qry->Fetch()) { $resource = new DAVResource($binding->dav_name); if ($resource->IsExternal()) { require_once "external-fetch.php"; update_external($resource); } if ($resource->HavePrivilegeTo('DAV::read', false)) { $resource->set_bind_location(str_replace($bound_from, $bound_to, $binding->dav_name)); $responses[] = $resource->RenderAsXML($property_list, $reply); if ($depth > 0) { $responses = array_merge($responses, get_collection_contents($depth - 1, $resource, $binding->dav_name)); } } } } $sql = 'SELECT principal.*, collection.*, \'collection\' AS type '; $sql .= 'FROM collection LEFT JOIN principal USING (user_no) '; $sql .= 'WHERE parent_container = :this_dav_name '; $sql .= ' ORDER BY collection_id'; $params[':this_dav_name'] = $bound_from; unset($params[':session_principal']); unset($params[':scan_depth']); } $qry = new AwlQuery($sql, $params); if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) { while ($subcollection = $qry->Fetch()) { $resource = new DAVResource($subcollection); if (!$resource->HavePrivilegeTo('DAV::read')) { continue; } $resource->set_bind_location(str_replace($bound_from, $bound_to, $subcollection->dav_name)); $responses[] = $resource->RenderAsXML($property_list, $reply); if ($depth > 0) { $responses = array_merge($responses, get_collection_contents($depth - 1, $resource, str_replace($resource->parent_path(), $parent_path, $resource->dav_name()))); } } } if ((!isset($c->disable_caldav_proxy) || $c->disable_caldav_proxy == false) && $collection->IsPrincipal()) { // Caldav Proxy: 5.1 par. 2: Add child resources calendar-proxy-(read|write) dbg_error_log('PROPFIND', 'Adding calendar-proxy-read and write. Path: %s', $bound_from); $response = add_proxy_response('read', $bound_from); if (isset($response)) { $responses[] = $response; } $response = add_proxy_response('write', $bound_from); if (isset($response)) { $responses[] = $response; } } } /** * freebusy permission is not allowed to see the items in a collection. Must have at least read permission. */ if ($collection->HavePrivilegeTo('DAV::read', false)) { dbg_error_log('PROPFIND', 'Getting collection items: Depth %d, Path: %s', $depth, $bound_from); $privacy_clause = ' '; $todo_clause = ' '; $time_limit_clause = ' '; if ($collection->IsCalendar()) { if (!$collection->HavePrivilegeTo('all', false)) { $privacy_clause = " 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'])) && !$collection->HavePrivilegeTo('all')) { $todo_clause = " AND caldav_data.caldav_type NOT IN ('VTODO') "; } if (isset($c->hide_older_than) && intval($c->hide_older_than > 0)) { $time_limit_clause = " 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) "; } } $sql = 'SELECT collection.*, principal.*, calendar_item.*, caldav_data.*, '; $sql .= "to_char(coalesce(calendar_item.created, caldav_data.created) at time zone 'GMT',{$date_format}) AS created, "; $sql .= "to_char(coalesce(calendar_item.last_modified, caldav_data.modified) at time zone 'GMT',{$date_format}) AS modified, "; $sql .= 'summary AS dav_displayname '; $sql .= 'FROM caldav_data LEFT JOIN calendar_item USING( dav_id, user_no, dav_name, collection_id) '; $sql .= 'LEFT JOIN collection USING(collection_id,user_no) LEFT JOIN principal USING(user_no) '; $sql .= 'WHERE collection.dav_name = :collection_dav_name ' . $time_limit_clause . ' ' . $todo_clause . ' ' . $privacy_clause; if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= " ORDER BY caldav_data.dav_id"; } $qry = new AwlQuery($sql, array(':collection_dav_name' => $bound_from)); if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) { while ($item = $qry->Fetch()) { if ($bound_from != $bound_to) { $item->bound_from = $item->dav_name; $item->dav_name = str_replace($bound_from, $bound_to, $item->dav_name); } $resource = new DAVResource($item); $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path); } } } return $responses; }
/** * Actually write the resource to the database. All checking of whether this is reasonable * should be done before this is called. * * @param DAVResource $resource The resource being written * @param string $caldav_data The actual data to be written * @param DAVResource $collection The collection containing the resource being written * @param int $author The user_no who wants to put this resource on the server * @param string $etag An etag unique for this event * @param string $put_action_type INSERT or UPDATE depending on what we are to do * @param boolean $caldav_context True, if we are responding via CalDAV, false for other ways of calling this * @param string Either 'INSERT' or 'UPDATE': the type of action we are doing * @param boolean $log_action Whether to log the fact that we are writing this into an action log (if configured) * @param string $weak_etag An etag that is NOT modified on ATTENDEE changes for this event * * @return boolean True for success, false for failure. */ function write_resource(DAVResource $resource, $caldav_data, DAVResource $collection, $author, &$etag, $put_action_type, $caldav_context, $log_action = true, $weak_etag = null) { global $tz_regex, $session; $path = $resource->bound_from(); $user_no = $collection->user_no(); $vcal = new vCalendar($caldav_data); $resources = $vcal->GetComponents('VTIMEZONE', false); // Not matching VTIMEZONE if (!isset($resources[0])) { $resource_type = 'Unknown'; /** @todo Handle writing non-calendar resources, like address book entries or random file data */ rollback_on_error($caldav_context, $user_no, $path, translate('No calendar content'), 412); return false; } else { $first = $resources[0]; if (!$first instanceof vComponent) { print $vcal->Render(); fatal('This is not a vComponent!'); } $resource_type = $first->GetType(); } $collection_id = $collection->collection_id(); $qry = new AwlQuery(); $qry->Begin(); $dav_params = array(':etag' => $etag, ':dav_data' => $caldav_data, ':caldav_type' => $resource_type, ':session_user' => $author, ':weak_etag' => $weak_etag); $calitem_params = array(':etag' => $etag); if ($put_action_type == 'INSERT') { $qry->QDo('SELECT nextval(\'dav_id_seq\') AS dav_id, null AS caldav_data'); } else { $qry->QDo('SELECT dav_id, caldav_data FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path)); } if ($qry->rows() != 1 || !($row = $qry->Fetch())) { // No dav_id? => We're toast! trace_bug('No dav_id for "%s" on %s!!!', $path, $create_resource ? 'create' : 'update'); rollback_on_error($caldav_context, $user_no, $path); return false; } $dav_id = $row->dav_id; $old_dav_data = $row->caldav_data; $dav_params[':dav_id'] = $dav_id; $calitem_params[':dav_id'] = $dav_id; $due = null; if ($first->GetType() == 'VTODO') { $due = $first->GetPValue('DUE'); } $calitem_params[':due'] = $due; $dtstart = $first->GetPValue('DTSTART'); if (empty($dtstart)) { $dtstart = $due; } $calitem_params[':dtstart'] = $dtstart; $dtend = $first->GetPValue('DTEND'); if (isset($dtend) && $dtend != '') { dbg_error_log('PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION')); $calitem_params[':dtend'] = $dtend; $dtend = ':dtend'; } else { // In this case we'll construct the SQL directly as a calculation relative to :dtstart $dtend = 'NULL'; if ($first->GetPValue('DURATION') != '' and $dtstart != '') { $duration = trim(preg_replace('#[PT]#', ' ', $first->GetPValue('DURATION'))); if ($duration == '') { $duration = '0 seconds'; } $dtend = '(:dtstart::timestamp with time zone + :duration::interval)'; $calitem_params[':duration'] = $duration; } elseif ($first->GetType() == 'VEVENT') { /** * From RFC2445 4.6.1: * For cases where a "VEVENT" calendar component specifies a "DTSTART" * property with a DATE data type but no "DTEND" property, the events * non-inclusive end is the end of the calendar date specified by the * "DTSTART" property. For cases where a "VEVENT" calendar component specifies * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property, * the event ends on the same calendar date and time of day specified by the * "DTSTART" property. * * So we're looking for 'VALUE=DATE', to identify the duration, effectively. * */ $dtstart_prop = $first->GetProperty('DTSTART'); $value_type = $dtstart_prop->GetParameterValue('VALUE'); dbg_error_log('PUT', 'DTSTART without DTEND. DTSTART value type is %s', $value_type); if (isset($value_type) && $value_type == 'DATE') { $dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)'; } else { $dtend = ':dtstart'; } } } $dtstamp = $first->GetPValue('DTSTAMP'); if (!isset($dtstamp) || $dtstamp == '') { // Strictly, we're dealing with an out of spec component here, but we'll try and survive $dtstamp = gmdate('Ymd\\THis\\Z'); } $calitem_params[':dtstamp'] = $dtstamp; $last_modified = $first->GetPValue('LAST-MODIFIED'); if (!isset($last_modified) || $last_modified == '') { $last_modified = $dtstamp; } $dav_params[':modified'] = $last_modified; $calitem_params[':modified'] = $last_modified; $created = $first->GetPValue('CREATED'); if ($created == '00001231T000000Z') { $created = '20001231T000000Z'; } $class = $first->GetPValue('CLASS'); /* Check and see if we should over ride the class. */ /** @todo is there some way we can move this out of this function? Or at least get rid of the need for the SQL query here. */ if (public_events_only($user_no, $path)) { $class = 'PUBLIC'; } /* * It seems that some calendar clients don't set a class... * RFC2445, 4.8.1.3: * Default is PUBLIC */ if (!isset($class) || $class == '') { $class = 'PUBLIC'; } $calitem_params[':class'] = $class; /** Calculate what timezone to set, first, if possible */ $last_olson = 'Turkmenikikamukau'; // I really hope this location doesn't exist! $tzid = GetTZID($first); if (!empty($tzid)) { $timezones = $vcal->GetComponents('VTIMEZONE'); foreach ($timezones as $k => $tz) { if ($tz->GetPValue('TZID') != $tzid) { /** * We'll skip any tz definitions that are for a TZID other than the DTSTART/DUE on the first VEVENT/VTODO */ dbg_error_log('ERROR', ' Event uses TZID[%s], skipping included TZID[%s]!', $tz->GetPValue('TZID'), $tzid); continue; } $olson = olson_from_tzstring($tzid); if (empty($olson)) { $olson = $tz->GetPValue('X-LIC-LOCATION'); if (!empty($olson)) { $olson = olson_from_tzstring($olson); } } } dbg_error_log('PUT', ' Using TZID[%s] and location of [%s]', $tzid, isset($olson) ? $olson : ''); if (!empty($olson) && $olson != $last_olson && preg_match($tz_regex, $olson)) { dbg_error_log('PUT', ' Setting timezone to %s', $olson); if ($olson != '') { $qry->QDo('SET TIMEZONE TO \'' . $olson . "'"); } $last_olson = $olson; } $params = array(':tzid' => $tzid); $qry = new AwlQuery('SELECT 1 FROM timezones WHERE tzid = :tzid', $params); if ($qry->Exec('PUT', __LINE__, __FILE__) && $qry->rows() == 0) { $params[':olson_name'] = $olson; $params[':vtimezone'] = isset($tz) ? $tz->Render() : null; $qry->QDo('INSERT INTO timezones (tzid, olson_name, active, vtimezone) VALUES(:tzid,:olson_name,false,:vtimezone)', $params); } if (!isset($olson) || $olson == '') { $olson = $tzid; } } $qry->QDo('SELECT new_sync_token(0,' . $collection_id . ')'); $calitem_params[':tzid'] = $tzid; $calitem_params[':uid'] = $first->GetPValue('UID'); $calitem_params[':summary'] = $first->GetPValue('SUMMARY'); $calitem_params[':location'] = $first->GetPValue('LOCATION'); $calitem_params[':transp'] = $first->GetPValue('TRANSP'); $calitem_params[':description'] = $first->GetPValue('DESCRIPTION'); $calitem_params[':rrule'] = $first->GetPValue('RRULE'); $calitem_params[':url'] = $first->GetPValue('URL'); $calitem_params[':priority'] = $first->GetPValue('PRIORITY'); $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE'); $calitem_params[':status'] = $first->GetPValue('STATUS'); if (!$collection->IsSchedulingCollection()) { if (do_scheduling_requests($vcal, $put_action_type == 'INSERT', $old_dav_data, true)) { $dav_params[':dav_data'] = $vcal->Render(null, true); $etag = null; } } if (!isset($dav_params[':modified'])) { $dav_params[':modified'] = 'now'; } if ($put_action_type == 'INSERT') { $sql = 'INSERT INTO caldav_data ( dav_id, user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag ) VALUES( :dav_id, :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, :created, :modified, :collection_id, :weak_etag )'; $dav_params[':collection_id'] = $collection_id; $dav_params[':user_no'] = $user_no; $dav_params[':dav_name'] = $path; $dav_params[':created'] = isset($created) && $created != '' ? $created : $dtstamp; } else { $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user, modified=:modified, weak_etag=:weak_etag WHERE dav_id=:dav_id'; } $qry = new AwlQuery($sql, $dav_params); if (!$qry->Exec('PUT', __LINE__, __FILE__)) { fatal('Insert into calendar_item failed...'); rollback_on_error($caldav_context, $user_no, $path); return false; } if ($put_action_type == 'INSERT') { $sql = <<<EOSQL INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp, description, rrule, tz_id, last_modified, url, priority, created, due, percent_complete, status, collection_id ) VALUES ( :user_no, :dav_name, :dav_id, :etag, :uid, :dtstamp, :dtstart, {$dtend}, :summary, :location, :class, :transp, :description, :rrule, :tzid, :modified, :url, :priority, :created, :due, :percent_complete, :status, :collection_id ) EOSQL; $sync_change = 201; $calitem_params[':collection_id'] = $collection_id; $calitem_params[':user_no'] = $user_no; $calitem_params[':dav_name'] = $path; $calitem_params[':created'] = $dav_params[':created']; } else { $sql = <<<EOSQL UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp, dtstart=:dtstart, dtend={$dtend}, summary=:summary, location=:location, class=:class, transp=:transp, description=:description, rrule=:rrule, tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority, due=:due, percent_complete=:percent_complete, status=:status WHERE dav_id=:dav_id EOSQL; $sync_change = 200; } write_alarms($dav_id, $first); write_attendees($dav_id, $vcal); if ($log_action && function_exists('log_caldav_action')) { log_caldav_action($put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path); } else { if ($log_action) { dbg_error_log('PUT', 'No log_caldav_action( %s, %s, %s, %s, %s) can be called.', $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path); } } $qry = new AwlQuery($sql, $calitem_params); if (!$qry->Exec('PUT', __LINE__, __FILE__)) { rollback_on_error($caldav_context, $user_no, $path); return false; } $qry->QDo("SELECT write_sync_change( {$collection_id}, {$sync_change}, :dav_name)", array(':dav_name' => $path)); $qry->Commit(); if (function_exists('post_commit_action')) { post_commit_action($put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path); } // Uncache anything to do with the collection $cache = getCacheInstance(); $cache_ns = 'collection-' . preg_replace('{/[^/]*$}', '/', $path); $cache->delete($cache_ns, null); dbg_error_log('PUT', 'User: %d, ETag: %s, Path: %s', $author, $etag, $path); return true; // Success! }
break; default: /** * @todo We should handle a lot more properties here. principal-URL seems a likely one to be used. * @todo We should catch the unsupported properties in the query and fire back an error indicating so. */ dbg_error_log("principal", "Unhandled tag '%s' to match '%s'\n", $v1->GetNSTag(), $match); } } if ($subwhere != "") { $where .= sprintf("%s(%s)", $where == "" ? "" : $clause_joiner, $subwhere); } } if ($where != "") { $where = "WHERE {$where}"; } $sql = "SELECT * FROM dav_principal {$where} ORDER BY principal_id LIMIT 100"; $qry = new AwlQuery($sql, $params); $get_props = $xmltree->GetPath('/DAV::principal-property-search/DAV::prop/*'); $properties = array(); foreach ($get_props as $k1 => $v1) { $properties[] = $v1->GetNSTag(); } if ($qry->Exec("REPORT", __LINE__, __FILE__) && $qry->rows() > 0) { while ($row = $qry->Fetch()) { $principal = new DAVResource($row); $responses[] = $principal->RenderAsXML($properties, $reply); } } $multistatus = new XMLElement("multistatus", $responses, $reply->GetXmlNsArray()); $request->XMLResponse(207, $multistatus);
$c->code_major = $matches[1]; $c->code_minor = $matches[2]; $c->code_patch = $matches[3]; $c->code_version = $c->code_major * 1000 + $c->code_minor . '.' . $c->code_patch; dbg_error_log('caldav', 'Version (%d.%d.%d) == %s', $c->code_major, $c->code_minor, $c->code_patch, $c->code_version); @header(sprintf('Server: %d.%d', $c->code_major, $c->code_minor)); } /** * Force the domain name to what was in the configuration file */ $_SERVER['SERVER_NAME'] = $c->domain_name; require_once 'AwlQuery.php'; $c->want_dbversion = array(1, 2, 11); $c->schema_version = 0; $qry = new AwlQuery('SELECT schema_major, schema_minor, schema_patch FROM awl_db_revision ORDER BY schema_id DESC LIMIT 1;'); if ($qry->Exec('always', __LINE__, __FILE__) && ($row = $qry->Fetch())) { $c->schema_version = doubleval(sprintf('%d%03d.%03d', $row->schema_major, $row->schema_minor, $row->schema_patch)); $c->wanted_version = doubleval(sprintf('%d%03d.%03d', $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2])); $c->schema_major = $row->schema_major; $c->schema_minor = $row->schema_minor; $c->schema_patch = $row->schema_patch; if ($c->schema_version < $c->wanted_version) { $c->messages[] = sprintf('Database schema needs upgrading. Current: %d.%d.%d, Desired: %d.%d.%d', $row->schema_major, $row->schema_minor, $row->schema_patch, $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]); } if (isset($c->default_timezone)) { $qry->QDo('SET TIMEZONE TO ?', $c->default_timezone); } } require_once 'Principal.php'; /** * Return the HTTP status code description for a given code. Hopefully