function rollback($response_code = 412) { global $request; $qry = new AwlQuery('ROLLBACK'); $qry->Exec('move'); // Just in case $request->DoResponse($response_code); // And we don't return from that. }
/** * 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; }
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; }
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(: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; }
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 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; }
/** * 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; } }
/** * 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.')); } }
/** * 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.')); } } }
/** * 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 }
/** * 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; }
$request->DoResponse(412); } if (isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag()) { $request->DoResponse(412); } $collection_id = $container->GetProperty('collection_id'); $qry = new AwlQuery(); $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 WHERE user_no=:user_no AND dav_name=:dav_name'; $response_code = 200; } else { $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, logged_user, created, modified, collection_id ) VALUES( :user_no, :dav_name, :etag, :dav_data, :session_user, current_timestamp, current_timestamp, :collection_id )'; $params[':collection_id'] = $collection_id; $response_code = 201; } $qry->QDo($sql, $params); $qry->QDo("SELECT write_sync_change( {$collection_id}, {$response_code}, :dav_name)", array(':dav_name' => $dest->bound_from())); $qry = new AwlQuery('COMMIT'); if (!$qry->Exec('move')) { rollback(500); } header('ETag: "' . $etag . '"'); if ($response_code == 200) { $response_code = 204; } $request->DoResponse($response_code);
} break; } } if ($ticket_timeout == 'infinity') { $sql_timeout = null; } else { if (preg_match('{^([a-z]+)-(\\d+)$}i', $ticket_timeout, $matches)) { /** It isn't specified, but timeout seems to be 'unit-number' like 'Seconds-3600', so we make it '3600 Seconds' which PostgreSQL understands */ $sql_timeout = $matches[2] . ' ' . $matches[1]; } else { $sql_timeout = $ticket_timeout; } } $collection_id = $target->GetProperty('collection_id'); $resource_id = $target->GetProperty('dav_id'); $i = 0; do { $ticket_id = substr(str_replace('/', '', str_replace('+', '', base64_encode(sha1(date('r') . rand(0, 2100000000) . microtime(true), true)))), 7, 8); $qry = new AwlQuery('INSERT INTO access_ticket ( ticket_id, dav_owner_id, privileges, target_collection_id, target_resource_id, expires ) VALUES( :ticket_id, :owner, :privs::INT::BIT(24), :collection, :resource, (current_timestamp + :expires::interval) )', array(':ticket_id' => $ticket_id, ':owner' => $session->principal_id, ':privs' => $ticket_privileges, ':collection' => $collection_id, ':resource' => $resource_id, ':expires' => $sql_timeout)); $result = $qry->Exec('MKTICKET', __LINE__, __FILE__); } while (!$result && $i++ < 2); $privs = new XMLElement('privilege'); foreach (bits_to_privilege($ticket_privileges) as $k => $v) { $reply->NSElement($privs, $v); } $ticketinfo = new XMLElement('T:ticketinfo', array(new XMLElement('T:id', $ticket_id), new XMLElement('owner', $reply->href(ConstructURL('/' . $session->username . '/'))), $privs, new XMLElement('T:timeout', $ticket_timeout), new XMLElement('T:visits', 'infinity'))); $prop = new XMLElement("prop", new XMLElement('T:ticketdiscovery', $ticketinfo), $reply->GetXmlNsArray()); header('Ticket: ' . $ticket_id); $request->XMLResponse(200, $prop);
function handle_remote_attendee_reply(vCalendar $ical) { $attendees = $ical->GetAttendees(); // attendee reply have just one attendee if (count($attendees) != 1) { return; } $attendee = $attendees[0]; $uidparam = $ical->GetPropertiesByPath("VCALENDAR/*/UID"); $uid = $uidparam[0]->Value(); $qry = new AwlQuery('UPDATE calendar_attendee SET email_status=:statusTo WHERE attendee=:attendee AND dav_id = (SELECT dav_id FROM calendar_item WHERE uid = :uid)'); // user accepted $qry->Bind(':statusTo', EMAIL_STATUS::NORMAL); $qry->Bind(':attendee', $attendee->Value()); $qry->Bind(':uid', $uid); $qry->Exec('changeStatusTo'); return true; }
$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
} $sql = 'SELECT calendar_item.*, addressbook_resource.*, caldav_data.* FROM caldav_data LEFT JOIN calendar_item USING(dav_id, user_no, dav_name, collection_id) LEFT JOIN addressbook_resource USING(dav_id) LEFT JOIN collection USING(collection_id)'; /** * @todo: Add stanzas for missing rows, so we don't just return a blank multistatus but * actually return <response> stanzas with a 404 for each absent href. We could do * this relatively easily with an array_flip($params) and remove each matching dav_name * as we process it. */ if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $where .= " ORDER BY caldav_data.dav_id"; } $qry = new AwlQuery($sql . $where, $params); if ($qry->Exec('REPORT', __LINE__, __FILE__) && $qry->rows() > 0) { while ($dav_object = $qry->Fetch()) { if ($bound_from != $collection->dav_name()) { $dav_object->dav_name = str_replace($bound_from, $collection->dav_name(), $dav_object->dav_name); } //if ( $need_expansion ) { $vResource = new vComponent($dav_object->caldav_data); $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end); // $event = $expanded->GetComponents("VEVENT")[0]; // // $attendeeName = "ATTENDEE"; // // $event->ClearProperties($attendeeName); // // $attendeeQry = new AwlQuery("SELECT params, attendee FROM calendar_attendee WHERE dav_id = :dav_id", array(':dav_id' => $dav_object->dav_id)); // $attendeeQry->Execute();
/** * 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; }
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(); }
*/ $sql = 'SELECT caldav_data, class, caldav_type, calendar_item.user_no, logged_user '; $sql .= 'FROM collection INNER JOIN caldav_data USING(collection_id) INNER JOIN calendar_item USING ( dav_id ) WHERE '; if (isset($c->get_includes_subcollections) && $c->get_includes_subcollections) { $sql .= '(collection.dav_name ~ :path_match '; $sql .= 'OR collection.collection_id IN (SELECT bound_source_id FROM dav_binding WHERE dav_binding.dav_name ~ :path_match)) '; $params = array(':path_match' => '^' . $request->path); } else { $sql .= 'caldav_data.collection_id = :collection_id '; $params = array(':collection_id' => $dav_resource->resource_id()); } if (isset($c->strict_result_ordering) && $c->strict_result_ordering) { $sql .= ' ORDER BY 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); } $need_zones = array(); $timezones = array(); while ($event = $qry->Fetch()) {
/** * 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; }
/** * 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; }
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) { 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());
/** * 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; }
* we use the xmlns="http://www.xythos.com/namespaces/StorageServer" rather * than the DAV: namespace. * * @package davical * @subpackage caldav * @author Andrew McMillan <*****@*****.**> * @copyright Morphoss Ltd - http://www.morphoss.com/ * @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later */ dbg_error_log('DELTICKET', 'method handler'); require_once 'DAVResource.php'; if (!$request->HavePrivilegeTo('DAV::unbind') && $request->ticket->owner() != $session->principal_id) { $request->NeedPrivilege('DAV::unbind'); } if (!isset($request->ticket)) { if (isset($_GET['ticket']) || isset($_SERVER['HTTP_TICKET'])) { $r = new DAVResource($request->path); if (!$r->Exists()) { $request->PreconditionFailed(404, 'not-found'); } else { $request->PreconditionFailed(412, 'ticket-does-not-exist', 'The specified ticket does not exist'); } } else { $request->MalformedRequest('No ticket specified'); } } $qry = new AwlQuery('DELETE FROM access_ticket WHERE ticket_id=:ticket_id', array(':ticket_id' => $request->ticket->id())); if ($qry->Exec('DELTICKET', __LINE__, __FILE__)) { $request->DoResponse(204); } $request->DoResponse(500);
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);
/** * * 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(); }
* A fresh lock */ $lock_token = uuid(); $sql = 'INSERT INTO locks ( dav_name, opaquelocktoken, type, scope, depth, owner, timeout, start ) VALUES( :dav_name, :lock_token, :type, :scope, :request_depth, :owner, :timeout::interval, current_timestamp )'; $params = array(':dav_name' => $request->path, ':lock_token' => $lock_token, ':type' => $lockinfo['type'], ':scope' => $lockinfo['scope'], ':request_depth' => $request->depth, ':owner' => $lockinfo['owner'], ':timeout' => $request->timeout . ' seconds'); header("Lock-Token: <opaquelocktoken:{$lock_token}>"); } $qry = new AwlQuery($sql, $params); $qry->Exec("LOCK", __LINE__, __FILE__); $lock_row = $request->GetLockRow($lock_token); $activelock = array(new XMLElement('locktype', new XMLElement($lock_row->type)), new XMLElement('lockscope', new XMLElement($lock_row->scope)), new XMLElement('depth', $request->GetDepthName()), new XMLElement('owner', new XMLElement('href', $lock_row->owner)), new XMLElement('timeout', 'Second-' . $request->timeout), new XMLElement('locktoken', new XMLElement('href', 'opaquelocktoken:' . $lock_token))); $response = new XMLElement("lockdiscovery", new XMLElement("activelock", $activelock), array("xmlns" => "DAV:")); } elseif ($request->method == "UNLOCK") { /** * @TODO: respond with preconditionfailed(409,'lock-token-matches-request-uri') if * there is no lock to be deleted. */ dbg_error_log("LOCK", "Attempting to unlock resource '%s'", $request->path); if ($lock_token = $request->IsLocked()) { // NOTE Assignment in if() is expected here. $sql = 'DELETE FROM locks WHERE opaquelocktoken = :lock_token'; $qry = new AwlQuery($sql, array(':lock_token' => $lock_token)); $qry->Exec("LOCK", __LINE__, __FILE__); } $request->DoResponse(204); } $prop = new XMLElement("prop", $response, array('xmlns' => 'DAV:')); // dbg_log_array( "LOCK", "XML", $response, true ); $xmldoc = $prop->Render(0, '<?xml version="1.0" encoding="utf-8" ?>'); $request->DoResponse(200, $xmldoc, 'text/xml; charset="utf-8"');
LEFT JOIN caldav_data USING (collection_id,dav_id) LEFT JOIN calendar_item USING (collection_id,dav_id) LEFT JOIN addressbook_resource USING (dav_id) WHERE collection.collection_id = :collection_id {$hide_older} {$hide_todo} AND sync_time >= (SELECT modification_time FROM sync_tokens WHERE sync_token = :sync_token) 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;