Kinda working with both Lightning & Evolution now. About to restructure to

add TODO items and make certain queries easier.
This commit is contained in:
Andrew McMillan 2006-09-21 18:24:26 +12:00
parent acc28c0faa
commit a57d690167
27 changed files with 2879 additions and 2107 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
caldav.session
rscds.session
build
rscds.bfproject

View File

@ -4,11 +4,10 @@
// $c->admin_email = 'andrew@catalyst.net.nz';
// $c->system_name = "Really Simple CalDAV Store";
$c->pg_connect[] = 'dbname=rscds port=5433 user=general';
$c->pg_connect[] = 'dbname=rscds port=5432 user=general';
$c->pg_connect[] = 'dbname=caldav port=5433 user=general';
$c->pg_connect[] = 'dbname=caldav port=5432 user=general';
$c->dbg['vevent'] = 1;
$c->dbg['put'] = 1;
$c->dbg['report'] = 1;
$c->dbg['ALL'] = 1;
$debuggroups['querystring'] = 1;
?>

View File

@ -5,31 +5,34 @@
\i /usr/share/awl/dba/awl-tables.sql
\i /usr/share/awl/dba/schema-management.sql
CREATE TABLE ics_event_data (
-- The main event. Where we store the things the calendar throws at us.
CREATE TABLE caldav_data (
user_no INT references usr(user_no),
ics_event_name TEXT,
ics_event_etag TEXT,
ics_raw_data TEXT,
dav_name TEXT,
dav_etag TEXT,
caldav_data TEXT,
caldav_type TEXT,
logged_user INT references usr(user_no),
PRIMARY KEY ( user_no, ics_event_name, ics_event_etag )
PRIMARY KEY ( user_no, vevent_name, vevent_etag )
);
GRANT SELECT,INSERT,UPDATE,DELETE ON ics_event_data TO general;
GRANT SELECT,INSERT,UPDATE,DELETE ON vevent_data TO general;
CREATE TABLE time_zones (
tzid TEXT PRIMARY KEY,
location TEXT,
tz_spec TEXT,
pgtz TEXT
-- Not particularly needed, perhaps, except as a way to collect
-- a bunch of valid iCalendar time zone specifications... :-)
CREATE TABLE time_zone (
tz_id TEXT PRIMARY KEY,
tz_locn TEXT,
tz_spec TEXT
);
GRANT SELECT,INSERT ON time_zones TO general;
GRANT SELECT,INSERT ON time_zone TO general;
CREATE TABLE ical_events (
-- The parsed event. Here we have pulled those events apart somewhat.
CREATE TABLE event (
user_no INT references usr(user_no),
ics_event_name TEXT,
ics_event_etag TEXT,
vevent_name TEXT,
vevent_etag TEXT,
-- Extracted vEvent event data
uid TEXT,
@ -42,12 +45,33 @@ CREATE TABLE ical_events (
transp TEXT,
description TEXT,
rrule TEXT,
tzid TEXT REFERENCES time_zones( tzid ),
tz_id TEXT REFERENCES time_zone( tz_id ),
-- Cascade updates / deletes from the ics_event_data table
CONSTRAINT ics_event_exists FOREIGN KEY ( user_no, ics_event_name, ics_event_etag )
REFERENCES ics_event_data ( user_no, ics_event_name, ics_event_etag )
-- Cascade updates / deletes from the vevent_data table
CONSTRAINT vevent_exists FOREIGN KEY ( user_no, vevent_name, vevent_etag )
REFERENCES vevent_data ( user_no, vevent_name, vevent_etag )
MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE
);
GRANT SELECT,INSERT,UPDATE,DELETE ON ical_events TO general;
GRANT SELECT,INSERT,UPDATE,DELETE ON event TO general;
-- Each user can be related to each other user. This mechanism can also
-- be used to define groups of users, since some relationships are transitive.
CREATE TABLE relationship_type (
rt_id SERIAL PRIMARY KEY,
rt_name TEXT,
rt_isgroup BOOLEAN,
rt_inverse INT,
confers TEXT DEFAULT 'RW',
prefix_match TEXT DEFAULT ''
);
GRANT SELECT,INSERT,UPDATE,DELETE ON relationship_type TO general;
CREATE TABLE relationship (
from_user INT REFERENCES usr (user_no) ON UPDATE CASCADE,
to_user INT REFERENCES usr (user_no) ON UPDATE CASCADE,
rt_id INT REFERENCES relationship_type (rt_id) ON UPDATE CASCADE
);
GRANT SELECT,INSERT,UPDATE,DELETE ON relationship TO general;

View File

@ -3,4 +3,67 @@
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 1, TRUE, current_date, current_date, 'admin', '**nimda', 'Calendar Administrator', 'calendars@example.net' );
SELECT setval('usr_user_no_seq', 1);
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 2, TRUE, current_date, current_date, 'andrew', '**x', 'Andrew McMillan', 'andrew@catalyst.net.nz' );
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 10, TRUE, current_date, current_date, 'user1', '**user1', 'User 1', 'user1@example.net' );
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 11, TRUE, current_date, current_date, 'user2', '**user2', 'User 2', 'user2@example.net' );
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 20, TRUE, current_date, current_date, 'manager1', '**manager1', 'Manager 1', 'manager1@example.net' );
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 30, TRUE, current_date, current_date, 'assistant1', '**assistant1', 'Assistant 1', 'assistant1@example.net' );
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 100, TRUE, current_date, current_date, 'resource1', '*salt*unpossible', 'Resource 1', 'resource1@example.net' );
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 101, TRUE, current_date, current_date, 'resource2', '*salt*unpossible', 'Resource 2', 'resource2@example.net' );
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 200, TRUE, current_date, current_date, 'resmgr1', '*salt*unpossible', 'Resource Managers', 'resource-managers@example.net' );
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 300, TRUE, current_date, current_date, 'teamclient1', '*salt*unpossible', 'Team for Client1', 'team-client1@example.net' );
SELECT setval('usr_user_no_seq', 1000);
INSERT INTO roles ( role_no, role_name ) VALUES( 1, 'Admin');
SELECT setval('roles_role_no_seq', 1);
INSERT INTO role_member (user_no, role_no) VALUES( 1, 1);
INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers, prefix_match )
VALUES( 1, 'Meeting Admin', TRUE, NULL, 'RW', '' );
INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers, prefix_match )
VALUES( 2, 'Assisted by', FALSE, 3, 'RW', '' );
INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers, prefix_match )
VALUES( 3, 'Assistant to', FALSE, 2, 'RW', '' );
INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers, prefix_match )
VALUES( 4, 'Team Member', FALSE, 4, 'R', '' );
INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, rt_inverse, confers, prefix_match )
VALUES( 5, 'Meeting Resource', TRUE, NULL, 'RW', '' );
-- The resources for meetings
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 200, 100, 5 );
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 200, 101, 5 );
-- The people who administer meetings
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 10, 200, 1 );
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 11, 200, 1 );
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30, 200, 1 );
-- Between a Manager and their PA
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 20, 30, 2 );
-- Between a team
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 20, 300, 4 );
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 10, 300, 4 );
INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30, 300, 4 );

View File

@ -5,6 +5,10 @@ require_once("BasicAuthSession.php");
$raw_headers = apache_request_headers();
$raw_post = file_get_contents ( 'php://input');
if ( $debugging && isset($_GET['method']) ) {
$_SERVER['REQUEST_METHOD'] = $_GET['method'];
}
switch ( $_SERVER['REQUEST_METHOD'] ) {
case 'OPTIONS':
include_once("caldav-OPTIONS.php");

View File

@ -3,6 +3,8 @@ require_once("always.php");
require_once("RSCDSSession.php");
$session->LoginRequired();
require_once("interactive-page.php");
$c->title = "Really Simple CalDAV Store - Configuration Help";
include("page-header.php");
@ -22,6 +24,6 @@ echo <<<EOBODY
</p>
EOBODY;
include("page-header.php");
include("page-footer.php");
?>

BIN
htdocs/images/down.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B

BIN
htdocs/images/up.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 B

View File

@ -3,6 +3,7 @@ require_once("always.php");
require_once("RSCDSSession.php");
$session->LoginRequired();
require_once("interactive-page.php");
include("page-header.php");
echo <<<EOBODY
@ -10,7 +11,7 @@ include("page-header.php");
<p>You appear to be logged on as $session->username ($session->fullname)</p>
<p>Useful links:
<ul>
<li><a href="/client_configuration_help.php">Help on configuring CalDAV clients with RSCDS</a></li>
<li><a href="/help.php">Help on configuring CalDAV clients with RSCDS</a></li>
</ul>
</p>
EOBODY;

44
htdocs/relationships.php Normal file
View File

@ -0,0 +1,44 @@
<?php
require_once("always.php");
require_once("RSCDSSession.php");
$session->LoginRequired();
require_once("interactive-page.php");
require_once("classBrowser.php");
$c->stylesheets[] = "css/browse.css";
$c->page_title = "User Relationships";
$browser = new Browser($c->page_title);
$browser->AddColumn( 'from_user', 'From', '', '##from_user_link##' );
$browser->AddColumn( 'from_name', 'Name', '', '', 'ufrom.fullname' );
$browser->AddHidden( 'from_user_link', "'<a href=\"/user.php?user_no=' || from_user || '\">' || from_user || '</a>'" );
$browser->AddColumn( 'to_user', 'From', '', '##to_user_link##' );
$browser->AddColumn( 'to_name', 'Name', '', '', 'uto.fullname' );
$browser->AddHidden( 'to_user_link', "'<a href=\"/user.php?user_no=' || to_user || '\">' || to_user || '</a>'" );
$browser->SetJoins( 'relationship JOIN usr ufrom ON (ufrom.user_no = from_user) JOIN usr uto ON (uto.user_no = to_user) ' );
if ( isset( $_GET['o']) && isset($_GET['d']) ) {
$browser->AddOrder( $_GET['o'], $_GET['d'] );
}
else
$browser->AddOrder( 'from_user', 'A' );
$browser->RowFormat( "<tr onMouseover=\"LinkHref(this,1);\" title=\"Click to Display Role Detail\" class=\"r%d\">\n", "</tr>\n", '#even' );
$browser->DoQuery();
// if ( $session->AllowedTo("Support") )
$relationship_menu->AddOption("New Relationship","/relationship.php?create","Add a new relationship", false, 10);
$active_menu_pattern = "#^/relationship#";
include("page-header.php");
echo $browser->Render();
include("page-footer.php");
?>

40
htdocs/roles.php Normal file
View File

@ -0,0 +1,40 @@
<?php
require_once("always.php");
require_once("RSCDSSession.php");
$session->LoginRequired();
require_once("interactive-page.php");
require_once("classBrowser.php");
$c->stylesheets[] = "css/browse.css";
$c->page_title = "System Roles";
$browser = new Browser($c->page_title);
$browser->AddColumn( 'role_no', 'No.', '', '##role_link##' );
$browser->AddColumn( 'role_name', 'Name' );
$browser->AddHidden( 'role_link', "'<a href=\"/role.php?role_no=' || role_no || '\">' || role_no || '</a>'" );
$browser->SetJoins( "roles" );
if ( isset( $_GET['o']) && isset($_GET['d']) ) {
$browser->AddOrder( $_GET['o'], $_GET['d'] );
}
else
$browser->AddOrder( 'role_no', 'A' );
$browser->RowFormat( "<tr onMouseover=\"LinkHref(this,1);\" title=\"Click to Display Role Detail\" class=\"r%d\">\n", "</tr>\n", '#even' );
$browser->DoQuery();
// if ( $session->AllowedTo("Admin") )
$role_menu->AddOption("New Role","/role.php?create","Add a new role", false, 10);
$active_menu_pattern = "#^/role#";
include("page-header.php");
echo $browser->Render();
include("page-footer.php");
?>

View File

@ -1,8 +1,106 @@
/* Basic overridable CSS for Really Simple CalDAV Store */
span.prompt {
body, p, li, td {
padding: 0;
margin: 0;
border: none;
}
.prompt {
font-weight: bold;
width: 12em;
padding-right: 1em;
display: block;
padding: 1px 1em 1px 0.5em;
text-align: right;
}
.error {
/* font-weight: bold; */
font-size: 110%;
font-family: Bitstream Vera Sans, Vera Sans, sans-serif;
padding: 1px 0.3em;
background-color: #c00000;
color: #ffff00;
}
h1, h2, h3, h4, h5 {
font-weight: bold;
font-family: Bitstream Vera Sans, Vera Sans, sans-serif;
padding-left: 0.3em;
padding-right: 0.3em;
padding-bottom: 0.1em;
}
h1 {
font-size: 200%;
background-color: #30c050;
margin: 0;
padding: 0.3em;
}
h1:first-letter {
font-size: 120%;
}
h2 {
font-size: 160%;
background-color: #60e080;
margin: 0;
padding-top: 0.3em;
margin-top: 1px;
}
p {
padding: 0.1em 0.3em 0.2em;
}
input.text, input.password, label {
background-color: ffffe0;
color: navy;
border: thin solid #000000;
padding: 3px;
}
label {
padding: 2px;
}
#menu {
background-color: #084010;
color: white;
font-weight: bold;
padding: 2px 1px;
margin-bottom: 1px;
}
#menu a.menu, #menu a.menu:hover, #menu a.menu_active, #menu a.menu_active:hover {
color: white;
text-decoration: none;
background-color: #10a020;
padding: 1px 0.2em;
margin-right: 0.2em;
}
#menu a.menu:hover, #menu a.menu_active:hover {
color: yellow;
background-color: #088010;
}
#submenu {
background-color: #105020;
color: white;
font-weight: bold;
padding: 2px 1px;
margin-bottom: 1px;
}
#submenu a.submenu, #submenu a.submenu:hover, #submenu a.submenu_active, #submenu a.submenu_active:hover {
color: white;
text-decoration: none;
background-color: #20c040;
padding: 1px 0.2em;
margin-right: 0.2em;
}
#submenu a.submenu:hover, #submenu a.submenu_active:hover {
color: yellow;
background-color: #10a020;
}

41
htdocs/users.php Normal file
View File

@ -0,0 +1,41 @@
<?php
require_once("always.php");
require_once("RSCDSSession.php");
$session->LoginRequired();
require_once("interactive-page.php");
require_once("classBrowser.php");
$c->stylesheets[] = "css/browse.css";
$browser = new Browser("Calendar Users");
$browser->AddColumn( 'user_no', 'No.', '', '##user_link##' );
$browser->AddColumn( 'username', 'Name' );
$browser->AddHidden( 'user_link', "'<a href=\"/user.php?user_no=' || user_no || '\">' || user_no || '</a>'" );
$browser->SetJoins( 'usr' );
if ( isset( $_GET['o']) && isset($_GET['d']) ) {
$browser->AddOrder( $_GET['o'], $_GET['d'] );
}
else
$browser->AddOrder( 'user_no', 'A' );
$browser->RowFormat( "<tr onMouseover=\"LinkHref(this,1);\" title=\"Click to Display Role Detail\" class=\"r%d\">\n", "</tr>\n", '#even' );
$browser->DoQuery();
$c->page_title = "Calendar Users";
// if ( $session->AllowedTo("Support") )
$user_menu->AddOption("New User","/user.php?create","Add a new user", false, 10);
$active_menu_pattern = "#^/user#";
include("page-header.php");
echo $browser->Render();
include("page-footer.php");
?>

View File

@ -9,8 +9,6 @@
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
require_once("session-util.php");
/**
* A Class for handling a session using HTTP Basic Authentication
*

View File

@ -109,15 +109,58 @@ class RSCDSSession extends Session
}
/**
* Internal function used to assign the session details to a user's new session.
* @param object $u The user+session object we (probably) read from the database.
*/
function AssignSessionDetails( $u ) {
parent::AssignSessionDetails( $u );
$this->GetRoles();
$this->GetRelationships();
}
/**
* 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 PgQuery( $sql );
if ( $qry->Exec('RSCDSSession') && $qry->rows > 0 ) {
while( $role = $qry->Fetch() ) {
$this->roles[$role->role_name] = 1;
}
}
}
/**
* Checks that this user is logged in, and presents a login screen if they aren't.
*
* The function can optionally confirm whether they are a member of one of a list
* of groups, and deny access if they are not a member of any of them.
*
* @param string $groups The list of groups that the user must be a member of one of to be allowed to proceed.
* @return boolean Whether or not the user is logged in and is a member of one of the required groups.
* Method used to get the user's relationships
*/
function GetRelationships () {
$this->relationships = array();
$sql = 'SELECT relationship.rt_id, rt_name, rt_isgroup, confers, prefix_match FROM relationship JOIN relationship_type USING (rt_id) WHERE from_user = '.$this->user_no.' UNION ';
$sql .= 'SELECT relationship_type.rt_id, rt_name, rt_isgroup, confers, prefix_match FROM relationship JOIN relationship_type ON (relationship.rt_id = relationship_type.rt_inverse) WHERE to_user = '.$this->user_no.';';
$qry = new PgQuery( $sql );
if ( $qry->Exec('RSCDSSession') && $qry->rows > 0 ) {
while( $relationship = $qry->Fetch() ) {
$this->relationships[$relationship->rt_id] = $relationship;
dbg_error_log( "RSCDSSession", "Relationships: %d - %s - %d - %s - %s -", $relationship->rt_id, $relationship->rt_name, $relationship->rt_isgroup, $relationship->confers, $relationship->prefix_match );
}
}
}
/**
* Checks that this user is logged in, and presents a login screen if they aren't.
*
* The function can optionally confirm whether they are a member of one of a list
* of groups, and deny access if they are not a member of any of them.
*
* @param string $groups The list of groups that the user must be a member of one of to be allowed to proceed.
* @return boolean Whether or not the user is logged in and is a member of one of the required groups.
*/
function LoginRequired( $groups = "" ) {
global $c, $session, $main_menu, $sub_menu, $tab_menu;
@ -129,6 +172,11 @@ class RSCDSSession extends Session
local_index_not_logged_in();
}
else {
if ( $this->login_failed ) {
echo <<<EOHTML
<p class="error">Invalid user name or password.</p>
EOHTML;
}
echo <<<EOHTML
<h1>Log On Please</h1>
<p>For access to the $c->system_name you should log on with

View File

@ -14,36 +14,15 @@ $c->sysabbr = 'rscds';
$c->admin_email = 'andrew@catalyst.net.nz';
$c->system_name = "Really Simple CalDAV Store";
$c->domain_name = $_SERVER['SERVER_NAME'];
$c->images = "/images";
$c->save_time_zone_defs = 1;
// Kind of private configuration values
$c->total_query_time = 0;
$c->dbg = array( 'core' => 1 );
if ( $debugging && isset($_GET['method']) ) {
$_SERVER['REQUEST_METHOD'] = $_GET['method'];
}
/**
* Writes a debug message into the error log using printf syntax
* @package rscds
*/
function dbg_error_log() {
global $c;
$argc = func_num_args();
$args = func_get_args();
$component = array_shift($args);
if ( !isset($c->dbg[strtolower($component)]) ) return;
if ( 2 <= $argc ) {
$format = array_shift($args);
}
else {
$format = "%s";
}
error_log( $c->sysabbr.": DBG: $component:". vsprintf( $format, $args ) );
}
$c->dbg = array( );
require_once("AWLUtilities.php");
dbg_error_log( "core", "==========> method =%s= =%s:%d= =%s= =%s=",
$_SERVER['REQUEST_METHOD'], $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $_SERVER['PATH_INFO']);
@ -58,45 +37,11 @@ else {
exit;
}
/**
* Attempt to connect to the configured connect strings
*/
$dbconn = false;
foreach( $c->pg_connect AS $k => $v ) {
if ( !$dbconn ) $dbconn = pg_Connect($v);
}
if ( ! $dbconn ) {
echo <<<EOERRMSG
<html><head><title>Database Error</title></head><body>
<h1>Database Error</h1>
<h3>Could not connect to PGPool or to Postgres</h3>
</body>
</html>
EOERRMSG;
exit;
}
/**
* Force the domain name to what was in the configuration file
*/
$_SERVER['SERVER_NAME'] = $c->domain_name;
if ( !function_exists('apache_request_headers') ) {
function apache_request_headers() {
return getallheaders();
}
}
function dbg_log_array( $component, $name, $arr, $recursive = false ) {
foreach ($arr as $key => $value) {
dbg_error_log( $component, "%s: >>%s<< = >>%s<<", $name, $key, $value);
if ( $recursive && (gettype($value) == 'array' || gettype($value) == 'object') ) {
dbg_log_array( $component, "$name"."[$key]", $value, $recursive );
}
}
}
include_once("PgQuery.php");
?>

View File

@ -7,9 +7,9 @@ dbg_error_log("delete", "DELETE method handler");
$get_path = $_SERVER['PATH_INFO'];
$etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]);
$qry = new PgQuery( "SELECT * FROM ics_event_data WHERE user_no = ? AND ics_event_name = ? AND ics_event_etag = ?;", $session->user_no, $get_path, $etag_none_match );
$qry = new PgQuery( "SELECT * FROM vevent_data WHERE user_no = ? AND vevent_name = ? AND vevent_etag = ?;", $session->user_no, $get_path, $etag_none_match );
if ( $qry->Exec("caldav-DELETE") && $qry->rows == 1 ) {
$qry = new PgQuery( "DELETE FROM ics_event_data WHERE user_no = ? AND ics_event_name = ? AND ics_event_etag = ?;", $session->user_no, $get_path, $etag_none_match );
$qry = new PgQuery( "DELETE FROM vevent_data WHERE user_no = ? AND vevent_name = ? AND vevent_etag = ?;", $session->user_no, $get_path, $etag_none_match );
if ( $qry->Exec("caldav-DELETE") ) {
header("HTTP/1.1 200 OK");
dbg_error_log( "delete", "DELETE: User: %d, ETag: %s, Path: %s", $session->user_no, $etag_none_match, $get_path);

View File

@ -7,21 +7,27 @@ dbg_error_log("get", "GET method handler");
$get_path = $_SERVER['PATH_INFO'];
$etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]);
$qry = new PgQuery( "SELECT * FROM ics_event_data WHERE user_no = ? AND ics_event_name = ? ;", $session->user_no, $get_path);
if ( $qry->Exec("caldav-GET") && $qry->rows == 1 ) {
$qry = new PgQuery( "SELECT * FROM vevent_data WHERE user_no = ? AND vevent_name = ? ;", $session->user_no, $get_path);
dbg_error_log("get", "%s", $qry->querystring );
if ( $qry->Exec("GET") && $qry->rows == 1 ) {
$event = $qry->Fetch();
header("HTTP/1.1 200 OK");
header("ETag: $event->ics_event_etag");
header("ETag: $event->vevent_etag");
header("Content-Type: text/calendar");
print $event->ics_raw_data;
print $event->vevent_data;
dbg_error_log( "GET", "User: %d, ETag: %s, Path: %s", $session->user_no, $event->ics_event_etag, $get_path);
dbg_error_log( "GET", "User: %d, ETag: %s, Path: %s", $session->user_no, $event->vevent_etag, $get_path);
}
else if ( $qry->rows != 1 ) {
header("HTTP/1.1 500 Internal Server Error");
dbg_error_log("ERROR", "Multiple rows match for User: %d, ETag: %s, Path: %s", $session->user_no, $event->vevent_etag, $get_path);
}
else {
header("HTTP/1.1 500 Infernal Server Error");
dbg_error_log("get", "Infernal Server Error");
}
?>

View File

@ -17,49 +17,74 @@ $etag_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
dbg_log_array( "PUT", 'HEADERS', $raw_headers );
dbg_log_array( "PUT", '_SERVER', $_SERVER, true );
if ( $etag_match == '*' || $etag_match == '' ) {
$qry = new PgQuery( "INSERT INTO ics_event_data ( user_no, ics_event_name, ics_event_etag, ics_raw_data ) VALUES( ?, ?, ?, ?)", $session->user_no, $put_path, $etag, $raw_post);
$qry->Exec("caldav-PUT");
header("HTTP/1.1 201 Created");
header("ETag: $etag");
}
else {
$qry = new PgQuery( "UPDATE ics_event_data SET ics_raw_data=?, ics_event_etag=? WHERE user_no=? AND ics_event_name=? AND ics_event_etag=?",
$raw_post, $etag, $session->user_no, $put_path, $etag_match );
$qry->Exec("caldav-PUT");
header("HTTP/1.1 201 Replaced");
header("ETag: $etag");
}
include_once("vEvent.php");
$ev = new vEvent(array( 'vevent' => $raw_post ));
dbg_log_array( "PUT", 'EVENT', $ev, true );
$sql = "SET TIMEZONE TO ".qpg($ev->tzlocn).";";
if ( $etag_match == '*' || $etag_match == '' ) {
/**
* If they didn't send an etag_match header, we need to check if the PUT object already exists
* and we are hence updating it. And we just set our etag_match to that.
*/
$qry = new PgQuery( "SELECT * FROM vevent_data WHERE user_no=? AND vevent_name=?", $session->user_no, $put_path );
$qry->Exec("PUT");
if ( $qry->rows > 1 ) {
header("HTTP/1.1 500 Infernal Server Error");
dbg_error_log("ERROR","Multiple events match replaced path for user %d, path %s", $session->user_no, $put_path );
exit(0);
}
elseif ( $qry->rows == 1 ) {
$event = $qry->Fetch();
$etag_match = $event->vevent_etag;
}
}
if ( $etag_match == '*' || $etag_match == '' ) {
/**
* If we got this far without an etag we must be inserting it.
*/
$qry = new PgQuery( "INSERT INTO vevent_data ( user_no, vevent_name, vevent_etag, vevent_data, logged_user ) VALUES( ?, ?, ?, ?, ?)",
$session->user_no, $put_path, $etag, $raw_post, $session->user_no );
$qry->Exec("PUT");
header("HTTP/1.1 201 Created");
header("ETag: $etag");
dbg_error_log( "PUT", "INSERT INTO vevent_data ( user_no, vevent_name, vevent_etag, vevent_data, logged_user ) VALUES( %d, '%s', '%s', '%s', %d)",
$session->user_no, $put_path, $etag, $raw_post, $session->user_no );
}
else {
$qry = new PgQuery( "UPDATE vevent_data SET vevent_data=?, vevent_etag=?, logged_user=? WHERE user_no=? AND vevent_name=? AND vevent_etag=?",
$raw_post, $etag, $session->user_no, $session->user_no, $put_path, $etag_match );
$qry->Exec("PUT");
header("HTTP/1.1 201 Replaced");
header("ETag: $etag");
}
$sql = "SET TIMEZONE TO ".qpg($ev->tz_locn).";";
if ( $etag_match == '*' || $etag_match == '' ) {
$sql .= <<<EOSQL
INSERT INTO ical_events (user_no, ics_event_name, ics_event_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp, description, rrule, tzid)
INSERT INTO event (user_no, vevent_name, vevent_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp, description, rrule, tz_id)
VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
EOSQL;
$qry = new PgQuery( $sql, $session->user_no, $put_path, $etag, $ev->Get('uid'), $ev->Get('dtstamp'),
$ev->Get('dtstart'), $ev->Get('dtend'), $ev->Get('summary'), $ev->Get('location'),
$ev->Get('class'), $ev->Get('transp'), $ev->Get('description'), $ev->Get('rrule'), $ev->Get('tzid') );
$qry->Exec("caldav-PUT");
$ev->Get('class'), $ev->Get('transp'), $ev->Get('description'), $ev->Get('rrule'), $ev->Get('tz_id') );
$qry->Exec("PUT");
}
else {
$sql = <<<EOSQL
UPDATE ical_events SET uid=?, dtstamp=?, dtstart=?, dtend=?, summary=?, location=?, class=?, transp=?, description=?, rrule=?, tzid=?
WHERE user_no=? AND ics_event_name=? AND ics_event_etag=?
UPDATE event SET uid=?, dtstamp=?, dtstart=?, dtend=?, summary=?, location=?, class=?, transp=?, description=?, rrule=?, tz_id=?
WHERE user_no=? AND vevent_name=? AND vevent_etag=?
EOSQL;
$qry = new PgQuery( $sql, $ev->Get('uid'), $ev->Get('dtstamp'), $ev->Get('dtstart'), $ev->Get('dtend'), $ev->Get('summary'),
$ev->Get('location'), $ev->Get('class'), $ev->Get('transp'), $ev->Get('description'), $ev->Get('rrule'),
$ev->Get('tzid'), $session->user_no, $put_path, $etag );
$qry->Exec("caldav-PUT");
$ev->Get('tz_id'), $session->user_no, $put_path, $etag );
$qry->Exec("PUT");
}
dbg_error_log( "PUT", "User: %d, ETag: %s, Path: %s", $session->user_no, $etag, $put_path);

View File

@ -2,16 +2,18 @@
dbg_error_log("REPORT", "method handler");
$attributes = array();
$parser = xml_parser_create_ns('UTF-8');
xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 );
function xml_start_callback( $parser, $el_name, $el_attrs ) {
dbg_error_log( "REPORT", "Parsing $el_name" );
// dbg_error_log( "REPORT", "Parsing $el_name" );
dbg_log_array( "REPORT", "$el_name::attrs", $el_attrs, true );
$attributes[$el_name] = $el_attrs;
}
function xml_end_callback( $parser, $el_name ) {
dbg_error_log( "REPORT", "Finished Parsing $el_name" );
// dbg_error_log( "REPORT", "Finished Parsing $el_name" );
}
xml_set_element_handler ( $parser, 'xml_start_callback', 'xml_end_callback' );
@ -20,25 +22,68 @@ $rpt_request = array();
xml_parse_into_struct( $parser, $raw_post, $rpt_request );
xml_parser_free($parser);
$reportnum = -1;
$report = array();
foreach( $rpt_request AS $k => $v ) {
switch ( $v['tag'] ) {
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA':
dbg_log_array( "REPORT", "CALENDAR-DATA", $v, true );
if ( $v['type'] == "complete" ) {
$report[$reportnum]['include_data'] = 1;
}
break;
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY':
dbg_log_array( "REPORT", "CALENDAR-QUERY", $v, true );
if ( $v['type'] == "open" ) {
$reportnum++;
$report_type = substr($v['tag'],30);
$report[$reportnum]['type'] = $report_type;
$report[$reportnum]['include_href'] = 1;
$report[$reportnum]['include_data'] = 1;
}
else {
unset($report_type);
}
break;
case 'URN:IETF:PARAMS:XML:NS:CALDAV:TIME-RANGE':
dbg_log_array( "REPORT", "TIME-RANGE", $v, true );
if ( isset($v['attributes']['START']) ) {
$report[$reportnum]['start'] = $v['attributes']['START'];
}
if ( isset($v['attributes']['END']) ) {
$report[$reportnum]['end'] = $v['attributes']['END'];
}
break;
case 'URN:IETF:PARAMS:XML:NS:CALDAV:COMP-FILTER':
dbg_log_array( "REPORT", "COMP-FILTER", $v, true );
if ( isset($v['attributes']['NAME']) && ($v['attributes']['NAME'] == 'VCALENDAR' )) {
$report[$reportnum]['calendar'] = 1;
}
if ( isset($v['attributes']['NAME']) ) {
if ( isset($report[$reportnum]['calendar']) && ($v['attributes']['NAME'] == 'VEVENT') ) {
$report[$reportnum]['calendar-event'] = 1;
}
if ( isset($report[$reportnum]['calendar']) && ($v['attributes']['NAME'] == 'VTODO') ) {
$report[$reportnum]['calendar-todo'] = 1;
}
if ( isset($report[$reportnum]['calendar']) && ($v['attributes']['NAME'] == 'VFREEBUSY') ) {
$report[$reportnum]['calendar-freebusy'] = 1;
}
}
break;
case 'URN:IETF:PARAMS:XML:NS:CALDAV:FILTER':
dbg_error_log( "REPORT", "Not using %s information which follows...", $v['tag'] );
dbg_log_array( "REPORT", "FILTER", $v, true );
break;
case 'DAV::PROP':
dbg_log_array( "REPORT", "DAV::PROP", $v, true );
if ( isset($report_type) ) {
if ( $v['type'] == "open" ) {
$report_properties = array();
@ -69,19 +114,33 @@ foreach( $rpt_request AS $k => $v ) {
}
}
// dbg_log_array( 'RPT', $rpt_request, true );
// dbg_log_array( 'REPORT', $report, true );
if ( $unsupported_stuff ) {
header('HTTP/1.1 403 Forbidden');
header('Content-Type: application/xml; charset="utf-8"');
echo <<<EOXML
<?xml version="1.0" encoding="utf-8" ?>
<D:error xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<C:supported-filter>
<C:prop-filter name="X-ABC-GUID"/>
</C:supported-filter>
</D:error>
EOXML;
exit(0);
}
header("HTTP/1.1 207 Multi-Status");
header("Content-type: text/xml;charset=UTF-8");
/**
* FIXME - this needs to be rewritten using XML libraries, in the same manner
* in which the REPORT request is parsed, in fact. For the time being we will
* attach importance to the care and feeding of Evolution, however.
*/
$response_tpl = <<<RESPONSETPL
<D:response>
<D:href>http://%s:%d%s%s</D:href>
<D:response>%s
<D:propstat>
<D:prop>
<D:getetag>"%s"</D:getetag>
<D:getetag>"%s"</D:getetag>%s
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
@ -89,6 +148,17 @@ $response_tpl = <<<RESPONSETPL
RESPONSETPL;
$calendar_href_tpl = <<<CALDATATPL
<D:href>http://%s:%d%s%s</D:href>
CALDATATPL;
$calendar_data_tpl = <<<CALDATATPL
<C:calendar-data>%s </C:calendar-data>
CALDATATPL;
dbg_log_array("REPORT", "report", $report, true );
echo <<<REPORTHDR
<?xml version="1.0" encoding="utf-8" ?>
@ -96,12 +166,38 @@ echo <<<REPORTHDR
REPORTHDR;
$qry = new PgQuery( "SELECT * FROM ics_event_data;" );
if ( $qry->Exec() && $qry->rows > 0 ) {
while( $event = $qry->Fetch() ) {
printf( $response_tpl, $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $event->ics_event_name, $event->ics_event_etag );
dbg_error_log("REPORT", "ETag >>%s<< >>http://%s:%s%s%s<<", $event->ics_event_etag,
$_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $event->ics_event_name);
for ( $i=0; $i <= $reportnum; $i++ ) {
dbg_error_log("REPORT", "Report[%d] Start:%s, End: %s, Events: %d, Todos: %d, Freebusy: %d",
$i, $report[$i]['start'], $report[$i]['end'], $report[$i]['calendar-event'], $report[$i]['calendar-todo'], $report[$i]['calendar-freebusy']);
if ( isset($report[$i]['calendar-event']) ) {
if ( isset($report[$i]['include_href']) ) dbg_error_log( "REPORT", "Returning href event data" );
if ( isset($report[$i]['include_data']) ) dbg_error_log( "REPORT", "Returning full event data" );
$sql = "SELECT * FROM vevent_data NATURAL JOIN event ";
$where = "";
if ( isset( $report[$i]['start'] ) ) {
$where = "WHERE dtend >= ".qpg($report[$i]['start'])."::timestamp with time zone ";
}
if ( isset( $report[$i]['end'] ) ) {
if ( $where != "" ) $where .= "AND ";
$where .= "dtstart <= ".qpg($report[$i]['end'])."::timestamp with time zone ";
}
$sql .= $where;
$qry = new PgQuery( $sql );
if ( $qry->Exec() && $qry->rows > 0 ) {
while( $event = $qry->Fetch() ) {
$calhref = ( isset($report[$i]['include_href']) ? sprintf( $calendar_href_tpl, $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $event->vevent_name ) : "" );
$caldata = ( isset($report[$i]['include_data']) ? sprintf( $calendar_data_tpl, $event->vevent_data ) : "" );
printf( $response_tpl, $calhref, $event->vevent_etag, $caldata );
dbg_error_log("REPORT", "ETag >>%s<< >>http://%s:%s%s%s<<", $event->vevent_etag,
$_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $event->vevent_name);
}
}
}
if ( isset($report[$i]['calendar-todo']) ) {
if ( isset($report[$i]['include_data']) ) dbg_error_log( "REPORT", "FIXME: Not returning full todo data" );
}
if ( isset($report[$i]['calendar-freebusy']) ) {
if ( isset($report[$i]['include_data']) ) dbg_error_log( "REPORT", "FIXME: Not returning full freebusy data" );
}
}

14
inc/interactive-page.php Normal file
View File

@ -0,0 +1,14 @@
<?php
require_once("session-util.php");
require_once("MenuSet.php");
$page_menu = new MenuSet('menu', 'menu', 'menu_active');
$page_menu->AddOption("Home","/","Browse all users", false, 3900 );
$page_menu->AddOption("Help","/help.php","Help on something or other", false, 4500 );
$page_menu->AddOption("Logout","/?logout","Log out of the $c->system_name", false, 5400 );
$relationship_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
$user_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
$role_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
$active_menu_pattern = '#^/(index.*)?$#'
?>

View File

@ -14,4 +14,11 @@ echo <<<EOHDR
<body>
EOHDR;
if ( isset($page_menu) && is_object($page_menu) ) {
$page_menu->AddSubMenu( $relationship_menu, "Relationships", "/relationships.php", "Browse all relationships", false, 4050 );
$page_menu->AddSubMenu( $user_menu, "Users", "/users.php", "Browse all users", false, 4100 );
$page_menu->AddSubMenu( $role_menu, "Roles", "/roles.php", "Browse all roles", false, 4300 );
$page_menu->MakeSomethingActive($active_menu_pattern);
echo $page_menu->Render();
}
?>

View File

@ -2,7 +2,7 @@
include("page-header.php");
echo <<<EOBODY
<h1>WRMS Not Configured</h1>
<h1>RSCDS Not Configured</h1>
<h2>The Bad News</h2>
<p>There is no configuration file present in <b>/etc/rscds/$_SERVER[SERVER_NAME]-conf.php</b> so
your installation is not fully set up.</p>

View File

@ -52,4 +52,35 @@ if ( !function_exists("session_validate_password") ) {
}
}
if ( !function_exists("replace_uri_params") ) {
/**
* Given a URL (presumably the current one) and a parameter, replace the value of parameter,
* extending the URL as necessary if the parameter is not already there.
* @param string $uri The URI we will be replacing parameters in.
* @param array $replacements An array of replacement pairs array( "replace_this" => "with this" )
* @return string The URI with the replacements done.
*/
function replace_uri_params( $uri, $replacements ) {
$replaced = $uri;
foreach( $replacements AS $param => $new_value ) {
$rxp = preg_replace( '/([\[\]])/', '\\\\$1', $param ); // Some parameters may be arrays.
$regex = "/([&?])($rxp)=([^&]+)/";
dbg_error_log("core", "Looking for [%s] to replace with [%s] regex is %s and searching [%s]", $param, $new_value, $regex, $replaced );
if ( preg_match( $regex, $replaced ) )
$replaced = preg_replace( $regex, "\$1$param=$new_value", $replaced);
else
$replaced .= "&$param=$new_value";
}
if ( ! preg_match( '/\?/', $replaced ) ) {
$replaced = preg_replace("/&(.+)$/", "?\$1", $replaced);
}
$replaced = str_replace("&amp;", "--AmPeRsAnD--", $replaced);
$replaced = str_replace("&", "&amp;", $replaced);
$replaced = str_replace("--AmPeRsAnD--", "&amp;", $replaced);
dbg_error_log("core", "URI <<$uri>> morphed to <<$replaced>>");
return $replaced;
}
}
?>

View File

@ -28,15 +28,15 @@ class vEvent {
/**
* An array of arbitrary properties
* @var props array
* @var properties array
*/
var $properties;
/**
* The typical name for the standard timezone
* @var tzname string
* The typical location name for the standard timezone such as "Pacific/Auckland"
* @var tz_locn string
*/
var $tzname;
var $tz_locn;
/**#@-*/
@ -49,7 +49,7 @@ class vEvent {
global $c;
// Probably a good idea to always have values for these things...
$this->properties['tzid'] = $c->local_tzid;
$this->properties['tz_id'] = $c->local_tzid;
$this->properties['modified'] = time();
$this->properties['sequence'] = 1;
$this->properties['uid'] = sprintf( "%s@%s", time() * 1000 + rand(0,1000), $c->domain_name);
@ -106,19 +106,16 @@ class vEvent {
if ( $state == 'BEGIN:VEVENT' && $state != $v ) {
list( $parameter, $value ) = preg_split('/:/', $v );
if ( preg_match('/^DT[A-Z]+;TZID=/', $parameter) ) {
list( $parameter, $tzid ) = preg_split('/;/', $parameter );
$properties['TZID'] = $tzid;
list( $parameter, $tz_id ) = preg_split('/;/', $parameter );
$properties['TZID'] = $tz_id;
}
$properties[strtoupper($parameter)] = $value;
}
if ( $state == 'BEGIN:VTIMEZONE' ) {
$vtimezone .= $v . "\n";
list( $parameter, $value ) = preg_split('/:/', $v );
if ( !isset($this->tzname) && $parameter == 'TZNAME' ) {
$this->tzname = $value;
}
if ( !isset($this->tzlocn) && $parameter == 'X-LIC-LOCATION' ) {
$this->tzlocn = $value;
if ( !isset($this->tz_locn) && $parameter == 'X-LIC-LOCATION' ) {
$this->tz_locn = $value;
}
}
}
@ -136,19 +133,18 @@ class vEvent {
* them into something that PostgreSQL can understand...
*/
function DealWithTimeZones() {
$qry = new PgQuery( "SELECT pgtz, location FROM time_zones WHERE tzid = ?;", $this->properties['TZID'] );
$qry = new PgQuery( "SELECT tz_locn FROM time_zone WHERE tz_id = ?;", $this->properties['TZID'] );
if ( $qry->Exec('vEvent') && $qry->rows == 1 ) {
$row = $qry->Fetch();
$this->tzname = $row->pgtz;
$this->tzlocn = $row->location;
$this->tz_locn = $row->tz_locn;
}
else {
if ( !isset($this->tzlocn) ) {
if ( !isset($this->tz_locn) ) {
// In case there was no X-LIC-LOCATION defined, let's hope there is something in the TZID
$this->tzlocn = preg_replace('/^.*([a-z]+\/[a-z]+)$/i','$1',$this->properties['TZID'] );
$this->tz_locn = preg_replace('/^.*([a-z]+\/[a-z]+)$/i','$1',$this->properties['TZID'] );
}
$qry2 = new PgQuery( "INSERT INTO time_zones (tzid, location, tz_spec, pgtz) VALUES( ?, ?, ?, ? );",
$this->properties['TZID'], $this->tzlocn, $this->properties['VTIMEZONE'], $this->tzname );
$qry2 = new PgQuery( "INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES( ?, ?, ? );",
$this->properties['TZID'], $this->tz_locn, $this->properties['VTIMEZONE'] );
$qry2->Exec("vEvent");
}
}

View File

@ -34,8 +34,12 @@
<item url="inc/RSCDSSession.php" uploadstatus="1" />
<item url="inc/page-header.php" uploadstatus="1" />
<item url="inc/page-footer.php" uploadstatus="1" />
<item url="inc/rscds_configuration_missing.php" />
<item url="htdocs/client_configuration_help.php" />
<item url="htdocs/rscds.css" />
<item url="inc/rscds_configuration_missing.php" uploadstatus="1" />
<item url="htdocs/rscds.css" uploadstatus="1" />
<item url="inc/interactive-page.php" uploadstatus="1" />
<item url="htdocs/users.php" uploadstatus="1" />
<item url="htdocs/help.php" uploadstatus="1" />
<item url="htdocs/roles.php" uploadstatus="1" />
<item url="htdocs/relationships.php" uploadstatus="1" />
</project>
</webproject>