Now storing events, including parsing them into further SQL.

This commit is contained in:
Andrew McMillan 2006-09-12 04:36:37 -05:00
parent 5c69ce125a
commit d64d2b4000
16 changed files with 110 additions and 98 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
caldav.session caldav.session
rscds.session rscds.session
build

View File

@ -15,4 +15,9 @@
} }
} }
$c->dbg['vevent'] = 1;
$c->dbg['put'] = 1;
$c->dbg['report'] = 1;
?> ?>

View File

@ -34,8 +34,8 @@ CREATE TABLE ical_events (
-- Extracted vEvent event data -- Extracted vEvent event data
uid TEXT, uid TEXT,
dtstamp TEXT, dtstamp TEXT,
dtstart TIMESTAMP, dtstart TIMESTAMP WITH TIME ZONE,
dtend TIMESTAMP, dtend TIMESTAMP WITH TIME ZONE,
summary TEXT, summary TEXT,
location TEXT, location TEXT,
class TEXT, class TEXT,

View File

@ -1,6 +1,6 @@
-- Some sample data to prime the database... -- Some sample data to prime the database...
INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email ) INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
VALUES( 1, TRUE, current_date, current_date, 'andrew', '**x', 'Andrew McMillan', 'andrew@catalyst.net.nz' ); VALUES( 1, TRUE, current_date, current_date, 'admin', '**nimda', 'Calendar Administrator', 'calendars@example.net' );
SELECT setval('usr_user_no_seq', 1); SELECT setval('usr_user_no_seq', 1);

1
htdocs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
andrew@catalyst.net.nz.freebusy

View File

@ -26,10 +26,10 @@ switch ( $_SERVER['REQUEST_METHOD'] ) {
break; break;
default: default:
dbg_error_log( "Unhandled request method >>%s<<", $_SERVER['REQUEST_METHOD'] ); dbg_error_log( "caldav", "Unhandled request method >>%s<<", $_SERVER['REQUEST_METHOD'] );
dbg_log_array( 'HEADERS', $raw_headers ); dbg_log_array( "caldav", 'HEADERS', $raw_headers );
dbg_log_array( '_SERVER', $_SERVER, true ); dbg_log_array( "caldav", '_SERVER', $_SERVER, true );
dbg_error_log( "RAW: %s", str_replace("\n", "",str_replace("\r", "", $raw_post)) ); dbg_error_log( "caldav", "RAW: %s", str_replace("\n", "",str_replace("\r", "", $raw_post)) );
} }

View File

@ -64,70 +64,11 @@ class BasicAuthSession {
header( sprintf( 'WWW-Authenticate: Basic realm="%s"', $c->system_name) ); header( sprintf( 'WWW-Authenticate: Basic realm="%s"', $c->system_name) );
header('HTTP/1.0 401 Unauthorized'); header('HTTP/1.0 401 Unauthorized');
echo 'Please log in for access to this system.'; echo 'Please log in for access to this system.';
dbg_error_log( "Login: User is not authorised" ); dbg_error_log( "Login", "User is not authorised" );
exit; exit;
} }
} }
/**
* Utility function to log stuff with printf expansion.
*
* This function could be expanded to log something identifying the session, but
* somewhat strangely this has not yet been done.
*
* @param string $whatever A log string
* @param mixed $whatever... Further parameters to be replaced into the log string a la printf
*/
function Log( $whatever )
{
global $c;
$argc = func_num_args();
$format = func_get_arg(0);
if ( $argc == 1 || ($argc == 2 && func_get_arg(1) == "0" ) ) {
error_log( "$c->sysabbr: $format" );
}
else {
$args = array();
for( $i=1; $i < $argc; $i++ ) {
$args[] = func_get_arg($i);
}
error_log( "$c->sysabbr: " . vsprintf($format,$args) );
}
}
/**
* Utility function to log debug stuff with printf expansion, and the ability to
* enable it selectively.
*
* The enabling is done by setting a variable "$debuggroups[$group] = 1"
*
* @param string $group The name of an arbitrary debug group.
* @param string $whatever A log string
* @param mixed $whatever... Further parameters to be replaced into the log string a la printf
*/
function Dbg( $whatever )
{
global $debuggroups, $c;
$argc = func_num_args();
$dgroup = func_get_arg(0);
if ( ! (isset($debuggroups[$dgroup]) && $debuggroups[$dgroup]) ) return;
$format = func_get_arg(1);
if ( $argc == 2 || ($argc == 3 && func_get_arg(2) == "0" ) ) {
error_log( "$c->sysabbr: DBG: $dgroup: $format" );
}
else {
$args = array();
for( $i=2; $i < $argc; $i++ ) {
$args[] = func_get_arg($i);
}
error_log( "$c->sysabbr: DBG: $dgroup: " . vsprintf($format,$args) );
}
}
/** /**
* CheckPassword does all of the password checking and * CheckPassword does all of the password checking and
@ -137,7 +78,7 @@ class BasicAuthSession {
$qry = new PgQuery( "SELECT * FROM usr WHERE lower(username) = ? ", $username ); $qry = new PgQuery( "SELECT * FROM usr WHERE lower(username) = ? ", $username );
if ( $qry->Exec('BAS::CheckPassword',__LINE,__FILE__) && $qry->rows == 1 ) { if ( $qry->Exec('BAS::CheckPassword',__LINE,__FILE__) && $qry->rows == 1 ) {
$usr = $qry->Fetch(); $usr = $qry->Fetch();
dbg_error_log( "Login: Name:%s, Pass:%s, File:%s", $username, $password, $usr->password ); dbg_error_log( "Login", "Name:%s, Pass:%s, File:%s", $username, $password, $usr->password );
if ( session_validate_password( $password, $usr->password ) ) { if ( session_validate_password( $password, $usr->password ) ) {
return $usr; return $usr;
} }

View File

@ -18,6 +18,8 @@ $c->domain_name = $_SERVER['SERVER_NAME'];
// Kind of private configuration values // Kind of private configuration values
$c->total_query_time = 0; $c->total_query_time = 0;
$c->dbg = array( 'core' => 1 );
if ( $debugging && isset($_GET['method']) ) { if ( $debugging && isset($_GET['method']) ) {
$_SERVER['REQUEST_METHOD'] = $_GET['method']; $_SERVER['REQUEST_METHOD'] = $_GET['method'];
} }
@ -30,17 +32,20 @@ function dbg_error_log() {
global $c; global $c;
$argc = func_num_args(); $argc = func_num_args();
$args = func_get_args(); $args = func_get_args();
$component = array_shift($args);
if ( !isset($c->dbg[strtolower($component)]) ) return;
if ( 2 <= $argc ) { if ( 2 <= $argc ) {
$format = array_shift($args); $format = array_shift($args);
} }
else { else {
$format = "%s"; $format = "%s";
} }
error_log( $c->sysabbr.": DBG: ". vsprintf( $format, $args ) ); error_log( $c->sysabbr.": DBG: $component:". vsprintf( $format, $args ) );
} }
dbg_error_log( "==========> method =%s= =%s:%d= =%s= =%s=", 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']); $_SERVER['REQUEST_METHOD'], $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $_SERVER['PATH_INFO']);
if ( file_exists("/etc/rscds/".$_SERVER['SERVER_NAME']."-conf.php") ) { if ( file_exists("/etc/rscds/".$_SERVER['SERVER_NAME']."-conf.php") ) {
include_once("/etc/rscds/".$_SERVER['SERVER_NAME']."-conf.php"); include_once("/etc/rscds/".$_SERVER['SERVER_NAME']."-conf.php");
@ -62,11 +67,11 @@ if ( !function_exists('apache_request_headers') ) {
} }
} }
function dbg_log_array( $name, $arr, $recursive = false ) { function dbg_log_array( $component, $name, $arr, $recursive = false ) {
foreach ($arr as $key => $value) { foreach ($arr as $key => $value) {
dbg_error_log( "%s: >>%s<< = >>%s<<", $name, $key, $value); dbg_error_log( $component, "%s: >>%s<< = >>%s<<", $name, $key, $value);
if ( $recursive && (gettype($value) == 'array' || gettype($value) == 'object') ) { if ( $recursive && (gettype($value) == 'array' || gettype($value) == 'object') ) {
dbg_log_array( "$name"."[$key]", $value, $recursive ); dbg_log_array( $component, "$name"."[$key]", $value, $recursive );
} }
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
dbg_error_log("DELETE method handler"); dbg_error_log("delete", "DELETE method handler");
// The DELETE method is not sent with any wrapping XML so we simply delete it // The DELETE method is not sent with any wrapping XML so we simply delete it
@ -12,16 +12,16 @@ 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 ics_event_data WHERE user_no = ? AND ics_event_name = ? AND ics_event_etag = ?;", $session->user_no, $get_path, $etag_none_match );
if ( $qry->Exec("caldav-DELETE") ) { if ( $qry->Exec("caldav-DELETE") ) {
header("HTTP/1.1 200 OK"); header("HTTP/1.1 200 OK");
dbg_error_log( "DELETE: User: %d, ETag: %s, Path: %s", $session->user_no, $etag_none_match, $get_path); dbg_error_log( "delete", "DELETE: User: %d, ETag: %s, Path: %s", $session->user_no, $etag_none_match, $get_path);
} }
else { else {
header("HTTP/1.1 500 Infernal Server Error"); header("HTTP/1.1 500 Infernal Server Error");
dbg_error_log( "DELETE failed: User: %d, ETag: %s, Path: %s, SQL: %s", $session->user_no, $etag_none_match, $get_path, $qry->querystring); dbg_error_log( "delete", "DELETE failed: User: %d, ETag: %s, Path: %s, SQL: %s", $session->user_no, $etag_none_match, $get_path, $qry->querystring);
} }
} }
else { else {
header("HTTP/1.1 404 Not Found"); header("HTTP/1.1 404 Not Found");
dbg_error_log( "DELETE row not found: User: %d, ETag: %s, Path: %s", $qry->rows, $session->user_no, $etag_none_match, $get_path); dbg_error_log( "delete", "DELETE row not found: User: %d, ETag: %s, Path: %s", $qry->rows, $session->user_no, $etag_none_match, $get_path);
} }
?> ?>

View File

@ -1,6 +1,6 @@
<?php <?php
dbg_error_log("GET method handler"); dbg_error_log("get", "GET method handler");
// The GET method is not sent with any wrapping XML so we simply fetch it // The GET method is not sent with any wrapping XML so we simply fetch it
@ -17,7 +17,7 @@ if ( $qry->Exec("caldav-GET") && $qry->rows == 1 ) {
print $event->ics_raw_data; print $event->ics_raw_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->ics_event_etag, $get_path);
} }
else { else {

View File

@ -1,5 +1,5 @@
<?php <?php
dbg_error_log("OPTIONS method handler"); dbg_error_log("OPTIONS", "method handler");
header( "Content-type: text/plain"); header( "Content-type: text/plain");
header( "Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE, PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL"); header( "Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE, PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL");
header( "DAV: 1, 2, 3, access-control, calendar-access"); header( "DAV: 1, 2, 3, access-control, calendar-access");

View File

@ -1,6 +1,6 @@
<?php <?php
dbg_error_log("PUT method handler"); dbg_error_log("PUT", "method handler");
// The PUT method is not sent with any wrapping XML so we simply store it // The PUT method is not sent with any wrapping XML so we simply store it
// after constructing an eTag and getting a name for it... // after constructing an eTag and getting a name for it...
@ -14,8 +14,8 @@ $put_path = $_SERVER['PATH_INFO'];
$etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]); $etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]);
$etag_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]); $etag_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
dbg_log_array( 'HEADERS', $raw_headers ); dbg_log_array( "PUT", 'HEADERS', $raw_headers );
dbg_log_array( '_SERVER', $_SERVER, true ); dbg_log_array( "PUT", '_SERVER', $_SERVER, true );
if ( $etag_match == '*' || $etag_match == '' ) { 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 = 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);
@ -33,6 +33,35 @@ else {
header("ETag: $etag"); header("ETag: $etag");
} }
dbg_error_log( "PUT: User: %d, ETag: %s, Path: %s", $session->user_no, $etag, $put_path); 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 == '' ) {
$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)
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");
}
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=?
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");
}
dbg_error_log( "PUT", "User: %d, ETag: %s, Path: %s", $session->user_no, $etag, $put_path);
?> ?>

View File

@ -1,17 +1,17 @@
<?php <?php
dbg_error_log("REPORT method handler"); dbg_error_log("REPORT", "method handler");
$parser = xml_parser_create_ns('UTF-8'); $parser = xml_parser_create_ns('UTF-8');
xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 ); xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 );
function xml_start_callback( $parser, $el_name, $el_attrs ) { 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( "$el_name::attrs", $el_attrs, true ); dbg_log_array( "REPORT", "$el_name::attrs", $el_attrs, true );
} }
function xml_end_callback( $parser, $el_name ) { 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' ); xml_set_element_handler ( $parser, 'xml_start_callback', 'xml_end_callback' );
@ -48,11 +48,11 @@ foreach( $rpt_request AS $k => $v ) {
unset($report_properties); unset($report_properties);
} }
else { else {
dbg_error_log( "REPORT: Unexpected DAV::PROP type of ".$v['type'] ); dbg_error_log( "REPORT", "Unexpected DAV::PROP type of ".$v['type'] );
} }
} }
else { else {
dbg_error_log( "REPORT: Unexpected DAV::PROP type of ".$v['type']." when no active report type."); dbg_error_log( "REPORT", "Unexpected DAV::PROP type of ".$v['type']." when no active report type.");
} }
break; break;
@ -65,7 +65,7 @@ foreach( $rpt_request AS $k => $v ) {
break; break;
default: default:
dbg_error_log("REPORT: Unhandled tag >>".$v['tag']."<<"); dbg_error_log( "REPORT", "Unhandled tag >>".$v['tag']."<<");
} }
} }
@ -100,7 +100,7 @@ REPORTHDR;
if ( $qry->Exec() && $qry->rows > 0 ) { if ( $qry->Exec() && $qry->rows > 0 ) {
while( $event = $qry->Fetch() ) { while( $event = $qry->Fetch() ) {
printf( $response_tpl, $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $event->ics_event_name, $event->ics_event_etag ); 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, 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); $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['SCRIPT_NAME'], $event->ics_event_name);
} }
} }

View File

@ -19,7 +19,7 @@ if ( !function_exists("session_salted_md5") ) {
*/ */
function session_salted_md5( $instr, $salt = "" ) { function session_salted_md5( $instr, $salt = "" ) {
if ( $salt == "" ) $salt = substr( md5(rand(100000,999999)), 2, 8); if ( $salt == "" ) $salt = substr( md5(rand(100000,999999)), 2, 8);
dbg_error_log( "Login: Making salted MD5: salt=$salt, instr=$instr, md5($salt$instr)=".md5($salt . $instr) ); dbg_error_log( "Login", "Making salted MD5: salt=$salt, instr=$instr, md5($salt$instr)=".md5($salt . $instr) );
return ( sprintf("*%s*%s", $salt, md5($salt . $instr) ) ); return ( sprintf("*%s*%s", $salt, md5($salt . $instr) ) );
} }
} }

View File

@ -32,6 +32,12 @@ class vEvent {
*/ */
var $properties; var $properties;
/**
* The typical name for the standard timezone
* @var tzname string
*/
var $tzname;
/**#@-*/ /**#@-*/
/** /**
@ -44,7 +50,7 @@ class vEvent {
// Probably a good idea to always have values for these things... // Probably a good idea to always have values for these things...
$this->properties['tzid'] = $c->local_tzid; $this->properties['tzid'] = $c->local_tzid;
$this->properties['modified'] = iCalendar::EpochTS(time()); $this->properties['modified'] = time();
$this->properties['sequence'] = 1; $this->properties['sequence'] = 1;
$this->properties['uid'] = sprintf( "%s@%s", time() * 1000 + rand(0,1000), $c->domain_name); $this->properties['uid'] = sprintf( "%s@%s", time() * 1000 + rand(0,1000), $c->domain_name);
$this->properties['guid'] = sprintf( "%s@%s", time() * 1000 + rand(0,1000), $c->domain_name); $this->properties['guid'] = sprintf( "%s@%s", time() * 1000 + rand(0,1000), $c->domain_name);
@ -76,7 +82,8 @@ class vEvent {
$vtimezone = ""; $vtimezone = "";
$state = 0; $state = 0;
foreach( $properties AS $k => $v ) { foreach( $lines AS $k => $v ) {
dbg_error_log( "vEvent", "LINE %03d: >>>%s<<<", $k, $v );
switch( $state ) { switch( $state ) {
case 0: case 0:
@ -102,10 +109,17 @@ class vEvent {
list( $parameter, $tzid ) = preg_split('/;/', $parameter ); list( $parameter, $tzid ) = preg_split('/;/', $parameter );
$properties['TZID'] = $tzid; $properties['TZID'] = $tzid;
} }
$properties[$parameter] = $value; $properties[strtoupper($parameter)] = $value;
} }
if ( $state == 'BEGIN:VTIMEZONE' ) { if ( $state == 'BEGIN:VTIMEZONE' ) {
$vtimezone .= $v . "\n"; $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;
}
} }
} }
@ -122,15 +136,30 @@ class vEvent {
* them into something that PostgreSQL can understand... * them into something that PostgreSQL can understand...
*/ */
function DealWithTimeZones() { function DealWithTimeZones() {
$qry = new PgQuery( "SELECT pgtz FROM time_zones WHERE tzid = ?;", $this->properties['TZID'] ); $qry = new PgQuery( "SELECT pgtz, location FROM time_zones WHERE tzid = ?;", $this->properties['TZID'] );
if ( $qry->Exec('vEvent') && $qry->rows == 1 ) { if ( $qry->Exec('vEvent') && $qry->rows == 1 ) {
$row = $qry->Fetch();
$this->tzname = $row->pgtz;
$this->tzlocn = $row->location;
} }
else { else {
$qry2 = new PgQuery( "INSERT INTO time_zones (tzid, location, tz_spec) VALUES( ?, ?, ?);", $this->properties['TZID'], $location, $this->properties['VTIMEZONE'] ); if ( !isset($this->tzlocn) ) {
// 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'] );
}
$qry2 = new PgQuery( "INSERT INTO time_zones (tzid, location, tz_spec, pgtz) VALUES( ?, ?, ?, ? );",
$this->properties['TZID'], $this->tzlocn, $this->properties['VTIMEZONE'], $this->tzname );
$qry2->Exec("vEvent"); $qry2->Exec("vEvent");
} }
} }
/**
* Get the value of a property
*/
function Get( $key ) {
return $this->properties[strtoupper($key)];
}
} }
?> ?>

View File

@ -29,5 +29,6 @@
<item url="debian/" uploadstatus="1" /> <item url="debian/" uploadstatus="1" />
<item url="debian/rules" uploadstatus="1" /> <item url="debian/rules" uploadstatus="1" />
<item url="dba/rscds.sql" /> <item url="dba/rscds.sql" />
<item url="htdocs/freebusy.php" />
</project> </project>
</webproject> </webproject>