mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-07-01 08:41:27 +00:00
Kinda working. Nearly packaged.
This commit is contained in:
parent
792b1a5083
commit
dc4526c3d7
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
caldav.session
|
||||
rscds.session
|
||||
9
TODO
Normal file
9
TODO
Normal file
@ -0,0 +1,9 @@
|
||||
Critical
|
||||
- accept the free/busy information as a PUT
|
||||
|
||||
Important
|
||||
- Parse the normal PUT requests so we know when time is available
|
||||
- Understand the repeat frequencies so ditto...
|
||||
|
||||
Nice
|
||||
- Some form of admin functionality
|
||||
@ -1,12 +0,0 @@
|
||||
<!DOCTYPE webprojectsession>
|
||||
<webprojectsession>
|
||||
<session usePreviewPrefix="0" previewPrefix="" >
|
||||
<itemcursorpositions/>
|
||||
<treestatus>
|
||||
<openfolder url="config" />
|
||||
<openfolder url="dba" />
|
||||
<openfolder url="htdocs" />
|
||||
<openfolder url="inc" />
|
||||
</treestatus>
|
||||
</session>
|
||||
</webprojectsession>
|
||||
@ -1,5 +1,8 @@
|
||||
<?php
|
||||
$c->domainname = "mycaldav.andrew";
|
||||
// $c->domainname = "mycaldav.andrew";
|
||||
// $c->sysabbr = 'rscds';
|
||||
// $c->admin_email = 'andrew@catalyst.net.nz';
|
||||
// $c->system_name = "Really Simple CalDAV Store";
|
||||
|
||||
if ( ! $dbconn = pg_Connect("port=5433 dbname=caldav user=general") ) {
|
||||
if ( ! $dbconn = pg_Connect("port=5432 dbname=caldav user=general") ) {
|
||||
|
||||
@ -9,6 +9,44 @@ CREATE TABLE ics_event_data (
|
||||
user_no INT references usr(user_no),
|
||||
ics_event_name TEXT,
|
||||
ics_event_etag TEXT,
|
||||
ics_raw_data TEXT
|
||||
ics_raw_data TEXT,
|
||||
|
||||
PRIMARY KEY ( user_no, ics_event_name, ics_event_etag )
|
||||
);
|
||||
|
||||
GRANT SELECT,INSERT,UPDATE,DELETE ON ics_event_data TO general;
|
||||
|
||||
|
||||
CREATE TABLE time_zones (
|
||||
tzid TEXT PRIMARY KEY,
|
||||
location TEXT,
|
||||
tz_spec TEXT
|
||||
);
|
||||
GRANT SELECT,INSERT ON time_zones TO general;
|
||||
|
||||
|
||||
CREATE TABLE ical_events (
|
||||
user_no INT references usr(user_no),
|
||||
ics_event_name TEXT,
|
||||
ics_event_etag TEXT,
|
||||
|
||||
-- Extracted iCalendar event data
|
||||
uid TEXT,
|
||||
dtstamp TEXT,
|
||||
dtstart TIMESTAMP,
|
||||
dtend TIMESTAMP,
|
||||
summary TEXT,
|
||||
location TEXT,
|
||||
class TEXT,
|
||||
transp TEXT,
|
||||
description TEXT,
|
||||
rrule TEXT,
|
||||
tzid TEXT REFERENCES time_zones( tzid ),
|
||||
|
||||
-- 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 )
|
||||
MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE
|
||||
);
|
||||
|
||||
GRANT SELECT,INSERT,UPDATE,DELETE ON ical_events TO general;
|
||||
|
||||
14
debian/README.debian
vendored
Normal file
14
debian/README.debian
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
RSCDS for Debian
|
||||
----------------
|
||||
|
||||
This is a Really Simple CalDAV Store which I wrote because I
|
||||
was getting sick of the length of time it was taking to make
|
||||
worthwhile CalDAV server-side implementations that worked OK
|
||||
with Evolution.
|
||||
|
||||
Then, when I finally did find a CalDAV store that worked, I
|
||||
found that it was quite bloated because it wanted to do vast
|
||||
amounts of irrelevant stuff. Well, irrelevant for me in any
|
||||
case.
|
||||
|
||||
Andrew McMillan <andrew@catalyst.net.nz>, Tue. 2 May 2006 07:11:22 +1200
|
||||
5
debian/changelog
vendored
Normal file
5
debian/changelog
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
rscds (0.1.0) unstable; urgency=low
|
||||
|
||||
* Initial Debian packaging
|
||||
|
||||
-- Andrew McMillan <debian@mcmillan.net.nz> Tue, 2 May 2006 07:43:59 +1200
|
||||
15
debian/control
vendored
Normal file
15
debian/control
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
Source: rscds
|
||||
Section: catalyst
|
||||
Priority: extra
|
||||
Maintainer: Andrew McMillan <andrew@catalyst.net.nz>
|
||||
Standards-Version: 3.5.9
|
||||
Build-Depends: debhelper
|
||||
|
||||
Package: rscds
|
||||
Architecture: all
|
||||
Depends: debconf (>= 1.0.32), php4 (>= 4:4.1), php4-pgsql(>= 3:4.1.0), postgresql-client (>= 7.2.1) | postgresql-client-8.0 | postgresql-client-8.1, libawl-php (>=0.1-1)
|
||||
Description: Really Simple CalDAV Server
|
||||
The Really Simple CalDAV Server is designed to trivially store
|
||||
CalDAV calendars, such as those from Evolution, in a central
|
||||
location, providing shared calendars, free/busy publication
|
||||
and basic administration (grouping, delegating, etc).
|
||||
3
debian/copyright
vendored
Normal file
3
debian/copyright
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
Andrew's Web Libraries are copyright 2006 Catalyst IT Ltd and
|
||||
are licensed under the Gnu Public License version 2, or later.
|
||||
|
||||
100
debian/rscds-phpdoc.ini
vendored
Normal file
100
debian/rscds-phpdoc.ini
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
;; phpDocumentor parse configuration file
|
||||
;;
|
||||
;; This file is designed to cut down on repetitive typing on the command-line or web interface
|
||||
;; You can copy this file to create a number of configuration files that can be used with the
|
||||
;; command-line switch -c, as in phpdoc -c default.ini or phpdoc -c myini.ini. The web
|
||||
;; interface will automatically generate a list of .ini files that can be used.
|
||||
;;
|
||||
;; default.ini is used to generate the online manual at http://www.phpdoc.org/docs
|
||||
;;
|
||||
;; ALL .ini files must be in the user subdirectory of phpDocumentor with an extension of .ini
|
||||
;;
|
||||
;; Copyright 2002, Greg Beaver <cellog@users.sourceforge.net>
|
||||
;;
|
||||
;; WARNING: do not change the name of any command-line parameters, phpDocumentor will ignore them
|
||||
|
||||
[Parse Data]
|
||||
;; title of all the documentation
|
||||
;; legal values: any string
|
||||
title = RSCDS
|
||||
|
||||
;; parse files that start with a . like .bash_profile
|
||||
;; legal values: true, false
|
||||
hidden = false
|
||||
|
||||
;; show elements marked @access private in documentation by setting this to on
|
||||
;; legal values: on, off
|
||||
parseprivate = off
|
||||
|
||||
;; parse with javadoc-like description (first sentence is always the short description)
|
||||
;; legal values: on, off
|
||||
javadocdesc = off
|
||||
|
||||
;; add any custom @tags separated by commas here
|
||||
;; legal values: any legal tagname separated by commas.
|
||||
;customtags = mytag1,mytag2
|
||||
|
||||
;; This is only used by the XML:DocBook/peardoc2 converter
|
||||
defaultcategoryname = Documentation
|
||||
|
||||
;; what is the main package?
|
||||
;; legal values: alphanumeric string plus - and _
|
||||
defaultpackagename = RSCDS
|
||||
|
||||
;; output any parsing information? set to on for cron jobs
|
||||
;; legal values: on
|
||||
;quiet = on
|
||||
|
||||
;; parse a PEAR-style repository. Do not turn this on if your project does
|
||||
;; not have a parent directory named "pear"
|
||||
;; legal values: on/off
|
||||
;pear = on
|
||||
|
||||
;; where should the documentation be written?
|
||||
;; legal values: a legal path
|
||||
target = /home/andrew/projects/rscds/debian/html
|
||||
|
||||
;; Which files should be parsed out as special documentation files, such as README,
|
||||
;; INSTALL and CHANGELOG? This overrides the default files found in
|
||||
;; phpDocumentor.ini (this file is not a user .ini file, but the global file)
|
||||
readmeinstallchangelog = README, INSTALL, CHANGELOG, NEWS, FAQ, LICENSE
|
||||
|
||||
;; limit output to the specified packages, even if others are parsed
|
||||
;; legal values: package names separated by commas
|
||||
;packageoutput = package1,package2
|
||||
|
||||
;; comma-separated list of files to parse
|
||||
;; legal values: paths separated by commas
|
||||
;filename = /path/to/file1,/path/to/file2,fileincurrentdirectory
|
||||
|
||||
;; comma-separated list of directories to parse
|
||||
;; legal values: directory paths separated by commas
|
||||
;directory = /path1,/path2,.,..,subdirectory
|
||||
;directory = /home/jeichorn/cvs/pear
|
||||
directory = /home/andrew/projects/rscds/inc,/home/andrew/projects/rscds/html
|
||||
|
||||
;; template base directory (the equivalent directory of <installdir>/phpDocumentor)
|
||||
;templatebase = /path/to/my/templates
|
||||
|
||||
;; directory to find any example files in through @example and {@example} tags
|
||||
;examplesdir = /path/to/my/templates
|
||||
examplesdir = /home/andrew/projects/rscds/examples
|
||||
|
||||
;; comma-separated list of files, directories or wildcards ? and * (any wildcard) to ignore
|
||||
;; legal values: any wildcard strings separated by commas
|
||||
;ignore = /path/to/ignore*,*list.php,myfile.php,subdirectory/
|
||||
ignore = api/,CVS
|
||||
|
||||
;; comma-separated list of Converters to use in outputformat:Convertername:templatedirectory format
|
||||
;; legal values: HTML:frames:default,HTML:frames:l0l33t,HTML:frames:phpdoc.de,HTML:frames:phphtmllib,
|
||||
;; HTML:frames:earthli,
|
||||
;; HTML:frames:DOM/default,HTML:frames:DOM/l0l33t,HTML:frames:DOM/phpdoc.de,
|
||||
;; HTML:frames:DOM/phphtmllib,HTML:frames:DOM/earthli
|
||||
;; HTML:Smarty:default,HTML:Smarty:PHP,HTML:Smarty:HandS
|
||||
;; PDF:default:default,CHM:default:default,XML:DocBook/peardoc2:default
|
||||
output=HTML:frames:earthli
|
||||
|
||||
;; turn this option on if you want highlighted source code for every file
|
||||
;; legal values: on/off
|
||||
sourcecode = on
|
||||
|
||||
55
debian/rules
vendored
Executable file
55
debian/rules
vendored
Executable file
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/make -f
|
||||
# Made with the aid of debmake, by Christoph Lameter,
|
||||
# based on the sample debian/rules file for GNU hello by Ian Jackson.
|
||||
|
||||
package=rscds
|
||||
dt=debian/$(package)
|
||||
|
||||
build: inc htdocs
|
||||
$(checkdir)
|
||||
cd ~andrew/projects/phpdoc/phpDocumentor-1.2.3 && ./phpdoc -c ~andrew/projects/$(package)/debian/$(package)-phpdoc.ini
|
||||
touch build
|
||||
|
||||
clean:
|
||||
$(checkdir)
|
||||
rm -f build
|
||||
rm -f `find . -name "*~"`
|
||||
-rm -rf $(dt) debian/files* core debian/substvars debian/html
|
||||
|
||||
binary-indep: checkroot build
|
||||
$(checkdir)
|
||||
-rm -rf $(dt)
|
||||
dh_clean -k
|
||||
# dh_installdebconf
|
||||
install -d $(dt) $(dt)/DEBIAN $(dt)/etc/$(package) \
|
||||
$(dt)/usr/share/$(package) $(dt)/usr/share/doc/$(package) \
|
||||
$(dt)/var/lib/$(package)
|
||||
cp -a htdocs $(dt)/usr/share/$(package)
|
||||
cp -a dba $(dt)/usr/share/$(package)
|
||||
cp -a inc $(dt)/usr/share/$(package)
|
||||
# cp -a admin $(dt)/usr/share/$(package)
|
||||
dh_installdocs
|
||||
dh_installchangelogs
|
||||
dh_fixperms
|
||||
install -m 1777 -d $(dt)/var/lib/$(package)/attachments
|
||||
dh_installdeb
|
||||
dpkg-gencontrol -P$(dt)
|
||||
dpkg --build $(dt) ..
|
||||
|
||||
binary-arch: checkroot build
|
||||
$(checkdir)
|
||||
# There are no architecture-dependent files to be uploaded
|
||||
# generated by this package. If there were any they would be
|
||||
# made here.
|
||||
|
||||
define checkdir
|
||||
test -f debian/rules
|
||||
endef
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
|
||||
checkroot:
|
||||
$(checkdir)
|
||||
test root = "`whoami`"
|
||||
|
||||
.PHONY: binary binary-arch binary-indep clean checkroot
|
||||
36
htdocs/caldav.php
Normal file
36
htdocs/caldav.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
require_once("always.php");
|
||||
|
||||
$raw_headers = apache_request_headers();
|
||||
$raw_post = file_get_contents ( 'php://input');
|
||||
|
||||
switch ( $_SERVER['REQUEST_METHOD'] ) {
|
||||
case 'OPTIONS':
|
||||
include_once("caldav-OPTIONS.php");
|
||||
break;
|
||||
|
||||
case 'REPORT':
|
||||
include_once("caldav-REPORT.php");
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
include_once("caldav-PUT.php");
|
||||
break;
|
||||
|
||||
case 'GET':
|
||||
include_once("caldav-GET.php");
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
include_once("caldav-DELETE.php");
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_error_log( "Unhandled request method >>%s<<", $_SERVER['REQUEST_METHOD'] );
|
||||
dbg_log_array( 'HEADERS', $raw_headers );
|
||||
dbg_log_array( '_SERVER', $_SERVER, true );
|
||||
dbg_error_log( "RAW: %s", str_replace("\n", "",str_replace("\r", "", $raw_post)) );
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
@ -1,47 +0,0 @@
|
||||
<?php
|
||||
require_once("always.php");
|
||||
|
||||
if ( !function_exists('apache_request_headers') ) {
|
||||
function apache_request_headers() {
|
||||
return getallheaders();
|
||||
}
|
||||
}
|
||||
|
||||
function dbg_log_array( $name, $arr, $recursive = false ) {
|
||||
foreach ($arr as $key => $value) {
|
||||
error_log( "caldav: DBG: $name: >>$key<< = >>$value<<");
|
||||
if ( $recursive && (gettype($value) == 'array' || gettype($value) == 'object') ) {
|
||||
dbg_log_array( "$name"."[$key]", $value, $recursive );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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");
|
||||
break;
|
||||
|
||||
case 'REPORT':
|
||||
include_once("caldav-REPORT.php");
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
include_once("caldav-PUT.php");
|
||||
break;
|
||||
|
||||
default:
|
||||
error_log("Unhandled request method >>".$_SERVER['REQUEST_METHOD']."<<");
|
||||
dbg_log_array( 'HEADERS', $raw_headers );
|
||||
dbg_log_array( '_SERVER', $_SERVER, true );
|
||||
error_log( "caldav: DBG: RAW: ".str_replace("\n", "", str_replace("\r", "", $raw_post)));
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
194
inc/BasicAuthSession.php
Normal file
194
inc/BasicAuthSession.php
Normal file
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
/**
|
||||
* A Class for handling BasicAuthSession data
|
||||
*
|
||||
* @package rscds
|
||||
* @subpackage BasicAuthSession
|
||||
* @author Andrew McMillan <andrew@catalyst.net.nz>
|
||||
* @copyright Catalyst IT Ltd
|
||||
* @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
|
||||
*
|
||||
* @package rscds
|
||||
*/
|
||||
class BasicAuthSession {
|
||||
/**#@+
|
||||
* @access private
|
||||
*/
|
||||
|
||||
/**
|
||||
* User ID number
|
||||
* @var user_no int
|
||||
*/
|
||||
var $user_no;
|
||||
|
||||
/**
|
||||
* User e-mail
|
||||
* @var email string
|
||||
*/
|
||||
var $email;
|
||||
|
||||
/**
|
||||
* User full name
|
||||
* @var fullname string
|
||||
*/
|
||||
var $fullname;
|
||||
|
||||
/**
|
||||
* Group rights
|
||||
* @var groups array
|
||||
*/
|
||||
var $groups;
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* The constructor, which pretty much drives it all
|
||||
*/
|
||||
function BasicAuthSession() {
|
||||
global $c;
|
||||
if ( isset($_SERVER['PHP_AUTH_USER']) ) {
|
||||
if ( $u = $this->CheckPassword( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
|
||||
$this->AssignSessionDetails($u);
|
||||
}
|
||||
else {
|
||||
unset($_SERVER['PHP_AUTH_USER']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
header( sprintf( 'WWW-Authenticate: Basic realm="%s"', $c->system_name) );
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
echo 'Please log in for access to this system.';
|
||||
dbg_error_log( "Login: User is not authorised" );
|
||||
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
|
||||
* returns a user record object, or false if it all ends in tears.
|
||||
*/
|
||||
function CheckPassword( $username, $password ) {
|
||||
$qry = new PgQuery( "SELECT * FROM usr WHERE lower(username) = ? ", $username );
|
||||
if ( $qry->Exec('BAS::CheckPassword',__LINE,__FILE__) && $qry->rows == 1 ) {
|
||||
$usr = $qry->Fetch();
|
||||
dbg_error_log( "Login: Name:%s, Pass:%s, File:%s", $username, $password, $usr->password );
|
||||
if ( session_validate_password( $password, $usr->password ) ) {
|
||||
return $usr;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a user is allowed to do something.
|
||||
*
|
||||
* The check is performed to see if the user has that role.
|
||||
*
|
||||
* @param string $whatever The role we want to know if the user has.
|
||||
* @return boolean Whether or not the user has the specified role.
|
||||
*/
|
||||
function AllowedTo ( $whatever ) {
|
||||
return ( $this->logged_in && isset($this->roles[$whatever]) && $this->roles[$whatever] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal function used to get the user's roles from the database.
|
||||
*/
|
||||
function GetRoles () {
|
||||
$this->roles = array();
|
||||
$qry = new PgQuery( 'SELECT role_name FROM role_member m join roles r ON r.role_no = m.role_no WHERE user_no = ? ', $this->user_no );
|
||||
if ( $qry->Exec('BAS::GetRoles') && $qry->rows > 0 ) {
|
||||
while( $role = $qry->Fetch() ) {
|
||||
$this->roles[$role->role_name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 ) {
|
||||
// Assign each field in the selected record to the object
|
||||
foreach( $u AS $k => $v ) {
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
|
||||
$this->GetRoles();
|
||||
$this->logged_in = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
$session = new BasicAuthSession();
|
||||
|
||||
?>
|
||||
@ -1,21 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* @package rscds
|
||||
* @author Andrew McMillan <andrew@catalyst.net.nz>
|
||||
* @copyright Catalyst IT Ltd
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
||||
*/
|
||||
|
||||
$c->sysabbr = 'caldav';
|
||||
// Ensure the configuration starts out as an empty object.
|
||||
unset($c);
|
||||
|
||||
// Default some of the configurable values
|
||||
$c->sysabbr = 'rscds';
|
||||
$c->admin_email = 'andrew@catalyst.net.nz';
|
||||
$c->system_name = "Andrew's CalDAV Server";
|
||||
$c->system_name = "Really Simple CalDAV Store";
|
||||
$c->domain_name = $_SERVER['SERVER_NAME'];
|
||||
|
||||
error_log( $c->sysabbr.": DBG: ======================================= Start $PHP_SELF for $HTTP_HOST on $_SERVER[SERVER_NAME]" );
|
||||
if ( file_exists("/etc/caldav/".$_SERVER['SERVER_NAME']."-conf.php") ) {
|
||||
include_once("/etc/caldav/".$_SERVER['SERVER_NAME']."-conf.php");
|
||||
// Kind of private configuration values
|
||||
$c->total_query_time = 0;
|
||||
|
||||
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();
|
||||
if ( 2 <= $argc ) {
|
||||
$format = array_shift($args);
|
||||
}
|
||||
else {
|
||||
$format = "%s";
|
||||
}
|
||||
error_log( $c->sysabbr.": DBG: ". vsprintf( $format, $args ) );
|
||||
}
|
||||
|
||||
|
||||
dbg_error_log( "==========> method =%s= =%s:%d= =%s= =%s=",
|
||||
$_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") ) {
|
||||
include_once("/etc/rscds/".$_SERVER['SERVER_NAME']."-conf.php");
|
||||
}
|
||||
else if ( file_exists("../config/config.php") ) {
|
||||
include_once("../config/config.php");
|
||||
}
|
||||
else {
|
||||
include_once("caldav_configuration_missing.php");
|
||||
include_once("rscds_configuration_missing.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
include_once("iCalendar.php");
|
||||
// In case this was forced 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( $name, $arr, $recursive = false ) {
|
||||
foreach ($arr as $key => $value) {
|
||||
dbg_error_log( "%s: >>%s<< = >>%s<<", $name, $key, $value);
|
||||
if ( $recursive && (gettype($value) == 'array' || gettype($value) == 'object') ) {
|
||||
dbg_log_array( "$name"."[$key]", $value, $recursive );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include_once("PgQuery.php");
|
||||
include_once("BasicAuthSession.php");
|
||||
// include_once("iCalendar.php");
|
||||
|
||||
?>
|
||||
27
inc/caldav-DELETE.php
Normal file
27
inc/caldav-DELETE.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
dbg_error_log("DELETE method handler");
|
||||
|
||||
// The DELETE method is not sent with any wrapping XML so we simply delete it
|
||||
|
||||
$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 );
|
||||
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 );
|
||||
if ( $qry->Exec("caldav-DELETE") ) {
|
||||
header("HTTP/1.1 200 OK");
|
||||
dbg_error_log( "DELETE: User: %d, ETag: %s, Path: %s", $session->user_no, $etag_none_match, $get_path);
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
|
||||
?>
|
||||
27
inc/caldav-GET.php
Normal file
27
inc/caldav-GET.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
dbg_error_log("GET method handler");
|
||||
|
||||
// The GET method is not sent with any wrapping XML so we simply fetch it
|
||||
|
||||
$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 ) {
|
||||
$event = $qry->Fetch();
|
||||
|
||||
header("HTTP/1.1 200 OK");
|
||||
header("ETag: $event->ics_event_etag");
|
||||
header("Content-Type: text/calendar");
|
||||
|
||||
print $event->ics_raw_data;
|
||||
|
||||
dbg_error_log( "GET: User: %d, ETag: %s, Path: %s", $session->user_no, $event->ics_event_etag, $get_path);
|
||||
|
||||
}
|
||||
else {
|
||||
header("HTTP/1.1 500 Infernal Server Error");
|
||||
}
|
||||
|
||||
?>
|
||||
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
error_log("caldav: DBG: OPTIONS method handler");
|
||||
dbg_error_log("OPTIONS method handler");
|
||||
header( "Content-type: text/plain");
|
||||
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");
|
||||
|
||||
@ -1,105 +1,38 @@
|
||||
<?php
|
||||
|
||||
error_log("caldav: DBG: PUT method handler");
|
||||
dbg_error_log("PUT method handler");
|
||||
|
||||
$parser = xml_parser_create_ns('UTF-8');
|
||||
xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 );
|
||||
// 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...
|
||||
|
||||
function xml_start_callback( $parser, $el_name, $el_attrs ) {
|
||||
error_log( "DBG: Parsing $el_name" );
|
||||
dbg_log_array( "$el_name::attrs", $el_attrs, true );
|
||||
$fh = fopen('/tmp/PUT.txt','w');
|
||||
fwrite($fh,$raw_post);
|
||||
fclose($fh);
|
||||
|
||||
$etag = md5($raw_post);
|
||||
$put_path = $_SERVER['PATH_INFO'];
|
||||
$etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]);
|
||||
$etag_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
|
||||
|
||||
dbg_log_array( 'HEADERS', $raw_headers );
|
||||
dbg_log_array( '_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");
|
||||
}
|
||||
|
||||
function xml_end_callback( $parser, $el_name ) {
|
||||
error_log( "DBG: Finished Parsing $el_name" );
|
||||
}
|
||||
|
||||
xml_set_element_handler ( $parser, 'xml_start_callback', 'xml_end_callback' );
|
||||
|
||||
$put_request = array();
|
||||
xml_parse_into_struct( $parser, $raw_post, $put_request );
|
||||
xml_parser_free($parser);
|
||||
|
||||
|
||||
$putnum = -1;
|
||||
$put = array();
|
||||
foreach( $put_request AS $k => $v ) {
|
||||
|
||||
switch ( $v['tag'] ) {
|
||||
|
||||
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-QUERY':
|
||||
if ( $v['type'] == "open" ) {
|
||||
$putnum++;
|
||||
$put_type = substr($v['tag'],30);
|
||||
$put[$putnum]['type'] = $put_type;
|
||||
}
|
||||
else {
|
||||
unset($put_type);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::PROP':
|
||||
if ( isset($put_type) ) {
|
||||
if ( $v['type'] == "open" ) {
|
||||
$put_properties = array();
|
||||
}
|
||||
else if ( $v['type'] == "close" ) {
|
||||
$put[$putnum]['properties'] = $put_properties;
|
||||
unset($put_properties);
|
||||
}
|
||||
else {
|
||||
error_log( "DBG: Unexpected DAV::PROP type of ".$v['type'] );
|
||||
}
|
||||
}
|
||||
else {
|
||||
error_log( "DBG: Unexpected DAV::PROP type of ".$v['type']." when no active put type.");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DAV::GETETAG':
|
||||
if ( isset($put_properties) ) {
|
||||
if ( $v['type'] == "complete" ) {
|
||||
$put_properties['GETETAG'] = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
error_log("caldav: DBG: Unhandled tag >>".$v['tag']."<<");
|
||||
}
|
||||
}
|
||||
|
||||
dbg_log_array( 'RPT', $put_request, true );
|
||||
|
||||
dbg_log_array( 'REPORT', $put, true );
|
||||
|
||||
header("Content-type: text/xml");
|
||||
|
||||
echo <<<EOXML
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:multistatus xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:response>
|
||||
<D:href>http://mycaldav/?andrewmcmillan.ics</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:getetag>"fffff-abcd1"</D:getetag>
|
||||
<C:calendar-data>BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Andrew's CalDAV Server//PHP//0.0.1
|
||||
EOXML;
|
||||
|
||||
echo iCalendar::vTimeZone("Pacific/Auckland");
|
||||
$event = new vEvent( '20060517T150000Z', 'PT2H', 'andrew@catalyst.net.nz', '', 'A Test Event for Andrew' );
|
||||
echo $event->ToString();
|
||||
|
||||
echo <<<EOXML
|
||||
END:VCALENDAR
|
||||
</C:calendar-data>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
</D:multistatus>
|
||||
EOXML;
|
||||
dbg_error_log( "PUT: User: %d, ETag: %s, Path: %s", $session->user_no, $etag, $put_path);
|
||||
|
||||
?>
|
||||
@ -1,20 +1,17 @@
|
||||
<?php
|
||||
error_log("caldav: DBG: REPORT method handler");
|
||||
|
||||
include_once("iCalendar.php");
|
||||
|
||||
error_reporting(E_ALL);
|
||||
dbg_error_log("REPORT method handler");
|
||||
|
||||
$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 ) {
|
||||
error_log( "DBG: Parsing $el_name" );
|
||||
dbg_error_log( "REPORT: Parsing $el_name" );
|
||||
dbg_log_array( "$el_name::attrs", $el_attrs, true );
|
||||
}
|
||||
|
||||
function xml_end_callback( $parser, $el_name ) {
|
||||
error_log( "DBG: Finished Parsing $el_name" );
|
||||
dbg_error_log( "REPORT: Finished Parsing $el_name" );
|
||||
}
|
||||
|
||||
xml_set_element_handler ( $parser, 'xml_start_callback', 'xml_end_callback' );
|
||||
@ -51,11 +48,11 @@ foreach( $rpt_request AS $k => $v ) {
|
||||
unset($report_properties);
|
||||
}
|
||||
else {
|
||||
error_log( "DBG: Unexpected DAV::PROP type of ".$v['type'] );
|
||||
dbg_error_log( "REPORT: Unexpected DAV::PROP type of ".$v['type'] );
|
||||
}
|
||||
}
|
||||
else {
|
||||
error_log( "DBG: 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;
|
||||
|
||||
@ -68,40 +65,47 @@ foreach( $rpt_request AS $k => $v ) {
|
||||
break;
|
||||
|
||||
default:
|
||||
error_log("caldav: DBG: Unhandled tag >>".$v['tag']."<<");
|
||||
dbg_error_log("REPORT: Unhandled tag >>".$v['tag']."<<");
|
||||
}
|
||||
}
|
||||
|
||||
dbg_log_array( 'RPT', $rpt_request, true );
|
||||
// dbg_log_array( 'RPT', $rpt_request, true );
|
||||
|
||||
dbg_log_array( 'REPORT', $report, true );
|
||||
// dbg_log_array( 'REPORT', $report, true );
|
||||
|
||||
header("Content-type: text/xml");
|
||||
header("HTTP/1.1 207 Multi-Status");
|
||||
header("Content-type: text/xml;charset=UTF-8");
|
||||
|
||||
echo <<<EOXML
|
||||
$response_tpl = <<<RESPONSETPL
|
||||
<D:response>
|
||||
<D:href>http://%s:%d%s%s</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:getetag>"%s"</D:getetag>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
|
||||
RESPONSETPL;
|
||||
|
||||
|
||||
echo <<<REPORTHDR
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:multistatus xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:response>
|
||||
<D:href>http://mycaldav/?andrewmcmillan.ics</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:getetag>"fffff-abcd1"</D:getetag>
|
||||
<C:calendar-data>BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Andrew's CalDAV Server//PHP//0.0.1
|
||||
EOXML;
|
||||
|
||||
echo iCalendar::vTimeZone("Pacific/Auckland");
|
||||
$event = new vEvent( '20060517T150000Z', 'PT2H', 'andrew@catalyst.net.nz', '', 'A Test Event for Andrew' );
|
||||
echo $event->ToString();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
echo <<<EOXML
|
||||
END:VCALENDAR
|
||||
</C:calendar-data>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
</D:multistatus>
|
||||
EOXML;
|
||||
|
||||
|
||||
136
inc/vEvent.php
Normal file
136
inc/vEvent.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* A Class for handling vEvent data
|
||||
*
|
||||
* @package rscds
|
||||
* @subpackage iCalendar
|
||||
* @author Andrew McMillan <andrew@catalyst.net.nz>
|
||||
* @copyright Catalyst IT Ltd
|
||||
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A Class for handling Events on a calendar
|
||||
*
|
||||
* @package rscds
|
||||
*/
|
||||
class vEvent {
|
||||
/**#@+
|
||||
* @access private
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of participants in this event
|
||||
* @var participants array
|
||||
*/
|
||||
var $participants = array();
|
||||
|
||||
/**
|
||||
* An array of arbitrary properties
|
||||
* @var props array
|
||||
*/
|
||||
var $properties;
|
||||
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* The constructor takes an array of args. If there is an element called 'vevent'
|
||||
* then that will be parsed into the vEvent object. Otherwise the array elements
|
||||
* are converted into properties of the vEvent object directly.
|
||||
*/
|
||||
function vEvent( $args ) {
|
||||
global $c;
|
||||
|
||||
// Probably a good idea to always have values for these things...
|
||||
$this->properties['tzid'] = $c->local_tzid;
|
||||
$this->properties['modified'] = iCalendar::EpochTS(time());
|
||||
$this->properties['sequence'] = 1;
|
||||
$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['duration'] = "PT1H";
|
||||
$this->properties['status'] = "TENTATIVE";
|
||||
|
||||
if ( !isset($args) || !is_array($args) ) return;
|
||||
|
||||
if ( isset($args['vevent']) ) {
|
||||
$this->BuildFromText($args['vevent']);
|
||||
$this->DealWithTimeZones();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach( $args AS $k => $v ) {
|
||||
$this->properties[strtoupper($k)] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the vEvent object from a text string which is a single VEVENT
|
||||
*
|
||||
* @var vevent string
|
||||
*/
|
||||
function BuildFromText( $vevent ) {
|
||||
$vevent = preg_replace('/[\r\n]+ /', ' ', $vevent );
|
||||
$lines = preg_split('/[\r\n]+/', $vevent );
|
||||
$properties = array();
|
||||
|
||||
$vtimezone = "";
|
||||
$state = 0;
|
||||
foreach( $properties AS $k => $v ) {
|
||||
|
||||
switch( $state ) {
|
||||
case 0:
|
||||
if ( $v == 'BEGIN:VEVENT' ) $state = $v;
|
||||
else if ( $v == 'BEGIN:VTIMEZONE' ) $state = $v;
|
||||
break;
|
||||
|
||||
case 'BEGIN:VEVENT':
|
||||
if ( $v == 'END:VEVENT' ) $state = 0;
|
||||
break;
|
||||
|
||||
case 'BEGIN:VTIMEZONE':
|
||||
if ( $v == 'END:VTIMEZONE' ) {
|
||||
$state = 0;
|
||||
$vtimezone .= $v;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
$properties[$parameter] = $value;
|
||||
}
|
||||
if ( $state == 'BEGIN:VTIMEZONE' ) {
|
||||
$vtimezone .= $v . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ( $vtimezone != "" ) {
|
||||
$properties['VTIMEZONE'] = $vtimezone;
|
||||
}
|
||||
|
||||
$this->properties = &$properties;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do what must be done with time zones from on file. Attempt to turn
|
||||
* them into something that PostgreSQL can understand...
|
||||
*/
|
||||
function DealWithTimeZones() {
|
||||
$qry = new PgQuery( "SELECT pgtz FROM time_zones WHERE tzid = ?;", $this->properties['TZID'] );
|
||||
if ( $qry->Exec('vEvent') && $qry->rows == 1 ) {
|
||||
}
|
||||
else {
|
||||
$qry2 = new PgQuery( "INSERT INTO time_zones (tzid, location, tz_spec) VALUES( ?, ?, ?);", $this->properties['TZID'], $location, $this->properties['VTIMEZONE'] );
|
||||
$qry2->Exec("vEvent");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@ -1,6 +1,6 @@
|
||||
<!DOCTYPE webproject>
|
||||
<webproject>
|
||||
<project type="Local" name="caldav" encoding="utf8" >
|
||||
<project type="Local" name="rscds" encoding="utf8" >
|
||||
<upload/>
|
||||
<author>Andrew McMillan</author>
|
||||
<email>andrew@catalyst.net.nz</email>
|
||||
@ -8,20 +8,26 @@
|
||||
<item url="" uploadstatus="1" />
|
||||
<templates>templates/</templates>
|
||||
<toolbars>toolbars/</toolbars>
|
||||
<item url="htdocs/index.php" uploadstatus="1" />
|
||||
<item url="htdocs/" uploadstatus="1" />
|
||||
<annotations/>
|
||||
<item url="inc/caldav-OPTIONS.php" uploadstatus="1" />
|
||||
<item url="inc/" uploadstatus="1" />
|
||||
<item url="inc/caldav-REPORT.php" uploadstatus="1" />
|
||||
<item url="inc/iCalendar.php" uploadstatus="1" />
|
||||
<item url="inc/always.php" uploadstatus="1" />
|
||||
<item url="config/config.php" uploadstatus="1" />
|
||||
<item url="config/" uploadstatus="1" />
|
||||
<item url="inc/caldav-PUT.php" uploadstatus="1" />
|
||||
<item url="dba/caldav.sql" />
|
||||
<item url="dba/" />
|
||||
<item url="dba/create-database.sh" />
|
||||
<item url="dba/sample-data.sql" />
|
||||
<item url="dba/caldav.sql" uploadstatus="1" />
|
||||
<item url="dba/" uploadstatus="1" />
|
||||
<item url="dba/create-database.sh" uploadstatus="1" />
|
||||
<item url="dba/sample-data.sql" uploadstatus="1" />
|
||||
<item url="inc/caldav-GET.php" uploadstatus="1" />
|
||||
<item url="inc/caldav-DELETE.php" uploadstatus="1" />
|
||||
<item url="htdocs/caldav.php" uploadstatus="1" />
|
||||
<item url="inc/BasicAuthSession.php" uploadstatus="1" />
|
||||
<item url="inc/session-util.php" uploadstatus="1" />
|
||||
<item url="inc/vEvent.php" uploadstatus="1" />
|
||||
<item url="debian/" />
|
||||
<item url="debian/rules" />
|
||||
</project>
|
||||
</webproject>
|
||||
Loading…
x
Reference in New Issue
Block a user