Resolve merge conflicts.

Conflicts:

	docs/website/inc/page-header.php
	docs/website/installation.php
This commit is contained in:
Andrew McMillan 2007-12-07 09:23:35 +13:00
commit 4f444d8bfa
236 changed files with 5710 additions and 1171 deletions

5
.gitignore vendored
View File

@ -1,8 +1,13 @@
caldav.session
rscds.session
davical.session
build
rscds.bfproject
davical.bfproject
locale
built-docs
built-po
.settings
*~
testing/dumps
testing/regression.conf

33
INSTALL
View File

@ -50,15 +50,15 @@ I'm available to answer questions, anyway :-)
Pre-requisites
==============
RSCDS depends on a number of things. Firstly, it depends
DAViCal depends on a number of things. Firstly, it depends
on Andrew's Web Libraries (AWL) which is a set of useful
PHP functions and objects written by Andrew McMillan over
a number of years.
The following other software is also needed:
Apache: 1.3.x or 2.x.x
PHP: 4.3 or greater, including PHP5
PostgreSQL: 7.4 or greater
PHP: 5.0 or greater
PostgreSQL: 8.1 or greater
The PostgreSQL database may be installed on a server other
than the web server, and that kind of situation is recommended
@ -102,22 +102,24 @@ Apache VHost Configuration
Your Apache instance needs to be configured for Virtual Hosts. If
this is not already the case you may want to read some documentation
about that, and you most likely will want to ensure that any existing
site becomes the **default** virtual host, with RSCDS only being a
site becomes the **default** virtual host, with DAViCal only being a
single virtual host.
I use a Virtual Host stanza like this:
#
# Virtual Host def for Debian packaged RSCDS
# Virtual Host def for Debian packaged DAViCal
<VirtualHost 123.4.56.78 >
DocumentRoot /usr/share/rscds/htdocs
DirectoryIndex index.php index.html
ServerName rscds.example.net
ServerName davical.example.net
ServerAlias calendar.example.net
Alias /images/ /usr/share/rscds/htdocs/images/
php_value include_path /usr/share/rscds/inc:/usr/share/awl/inc
php_value magic_quotes_gpc 0
php_value register_globals 1
php_value register_globals 0
php_value error_reporting "E_ALL & ~E_NOTICE"
php_value default_charset "utf-8"
</VirtualHost>
Replace 123.4.56.78 with your own IP address, of course (you can
@ -135,25 +137,24 @@ installed from a package.
Once your VHost is installed an working correctly, you should be
able to browse to that address and see a page telling you that
you need to configure RSCDS.
you need to configure DAViCal.
RSCDS Configuration
===================
DAViCal Configuration
=====================
The RSCDS configuration generally resides in /etc/rscds/<domain>-conf.php
The DAViCal configuration generally resides in /etc/davical/<domain>-conf.php
and is a regular PHP file which sets (or overrides) some specific variables.
<?php
// $c->domainname = "calendar.example.net";
// $c->sysabbr = 'rscds';
// $c->sysabbr = 'davical';
// $c->admin_email = 'admin@example.net';
// $c->system_name = "Really Simple CalDAV Store";
// $c->system_name = "DAViCal CalDAV Server";
// $c->collections_always_exist = false;
$c->pg_connect[] = 'dbname=caldav port=5433 user=general';
$c->pg_connect[] = 'dbname=caldav port=5432 user=general';
$c->pg_connect[] = 'dbname=davical port=5432 user=general';
?>
@ -180,7 +181,7 @@ If all is going well you should now be able to browse to the admin
pages and log in as 'admin' (the password is the bit after the '**'
in the 'password' field of the 'usr' table so:
psql rscds -c 'select username, password from usr;'
psql davical -c 'select username, password from usr;'
should show you a list. Note that once you change a password it
won't be readable in this way - only the initial configuration

View File

@ -4,12 +4,16 @@
package=rscds
version=$(shell cat VERSION)
all: inc/always.php built-docs
all: inc/always.php built-docs built-po
built-docs: docs/api/phpdoc.ini htdocs/*.php inc/*.php
phpdoc -c docs/api/phpdoc.ini
phpdoc -c docs/api/phpdoc.ini || echo "WARNING: failed to build docs"
touch built-docs
built-po: inc/always.php scripts/po/rebuild-translations.sh scripts/po/extract.pl
scripts/po/rebuild-translations.sh
touch built-po
#
# Insert the current version number into always.php
#
@ -28,7 +32,7 @@ release: built-docs
rm $(package)-$(version)
clean:
rm -f built-docs
rm -f built-docs built-po
-find docs/api/* ! -name "phpdoc.ini" ! -name ".gitignore" -delete
-find . -name "*~" -delete

10
README
View File

@ -1 +1,9 @@
Really Simple CalDAV Store by Andrew McMillan.
DAViCal CalDAV Server by Andrew McMillan.
For documentation you are best advised to visit the sourceforge pages
or to start searching from http://davical.org/ and see where you end
up.
Good luck!
Andrew McMillan

10
TODO
View File

@ -1,10 +1,12 @@
Desirable
- accept the free/busy information as a PUT
- accept the free/busy information as a POST
- more translations of the administration interface
- translations of the website.
- the ability to see a basic list of event data in the admin interface
- the ability to see a better list of event data in the admin interface
- allow a specific CalDAV access permission to delegate free/busy viewability.
- rename everything to "DAViCal"
Important
- allow for some sort of modular authentication methods
-
- Fix the relationships code so relationships are controlled by the person
affected (or the admin).
- Implement draft-desruisseaux-caldav-sched specifications.

View File

@ -1 +1 @@
0.9.0
0.9.2

View File

@ -6,7 +6,7 @@
*/
/**
* if this is set then any e-mail that would normally be sent by RSCDS will be
* if this is set then any e-mail that would normally be sent by DAViCal will be
* sent to this e-mail address for debugging.
*/
//$c->debug_email
@ -29,21 +29,23 @@
// $c->dbg['querystring'] = 1;
// $c->dbg['icalendar'] = 1;
// $c->dbg['ics'] = 1;
// $c->dbg['Login'] = 1;
// $c->dbg['login'] = 1;
// $c->dbg['options'] = 1;
// $c->dbg['get'] = 1;
// $c->dbg['put'] = 1;
// $c->dbg['propfind'] = 1;
// $c->dbg['proppatch'] = 1;
// $c->dbg['report'] = 1;
// $c->dbg['principal'] = 1;
// $c->dbg['user'] = 1;
// $c->dbg['vevent'] = 1;
// $c->dbg['rrule'] = 1;
/**
* default is 'rscds' used to prefix debugging messages but will only need to change
* if you are running multiple RSCDS servers logging into the same place.
* default is 'davical' used to prefix debugging messages but will only need to change
* if you are running multiple DAViCal servers logging into the same place.
*/
// $c->sysabbr
// $c->sysabbr = 'davical';
/**
* As yet we only support quite a limited range of options. When we see clients looking
@ -55,4 +57,3 @@
*/
// $c->override_allowed_methods = "PROPPATCH, OPTIONS, GET, HEAD, PUT, DELETE, PROPFIND, MKCOL, MKCALENDAR, LOCK, UNLOCK, REPORT"
?>

View File

@ -10,14 +10,14 @@
*****************************/
/**
* Ex : $c->pg_connect[] = 'dbname=rscds port=5432 user=general'
* Ex : $c->pg_connect[] = 'dbname=davical port=5432 user=general'
* The application will attempt to
* connect to the database, successively applying connection parameters from
* the array in $c->pg_connect.
* used in the web interface but also the caldav Server
*/
$c->pg_connect[] = "dbname=rscds user=general";
// $c->pg_connect[] = "dbname=rscds user=general port=5433 host=somehost password=mypass";
$c->pg_connect[] = "dbname=davical user=general";
// $c->pg_connect[] = "dbname=davical user=general port=5433 host=somehost password=mypass";
/****************************
@ -66,7 +66,7 @@ $c->admin_email ='calendar-admin@example.com';
* <p>The "enable_row_linking" option controls whether javascript is used
* to make the entire row clickable in browse lists in the administration
* pages. Since this doesn't work in Konqueror you may want to set this
* to false if you expect people to be using Konqueror with the RSCDS
* to false if you expect people to be using Konqueror with the DAViCal
* administration pages.</p>
*/
// $c->enable_row_linking = true;
@ -149,19 +149,20 @@ $c->admin_email ='calendar-admin@example.com';
/********************************/
/******* Other AWL hook *********/
/********************************/
//require_once('AuthPlugins.php');
// require_once('auth-functions.php');
// $c->authenticate_hook = array(
// 'call' => 'auth_other_awl',
// 'call' => 'AuthExternalAwl',
// 'config' => array(
/** A PgSQL database connection string for the database containing user records */
// 'connection' => 'dbname=wrms host=otherhose port=5433 user=general',
/** Which columns should be fetched from the database */
// 'columns' => "user_no, active, email_ok, joined, last_update AS updated, last_used, username, password, fullname, email"
// // A PgSQL database connection string for the database containing user records
// 'connection' => 'dbname=wrms host=otherhost port=5433 user=general',
// // Which columns should be fetched from the database
// 'columns' => "user_no, active, email_ok, joined, last_update AS updated, last_used, username, password, fullname, email",
// // a WHERE clause to limit the records returned.
// 'where' => "active AND org_code=7"
// )
// );
/********************************/
/*********** LDAP hook **********/
/********************************/
@ -169,8 +170,12 @@ $c->admin_email ='calendar-admin@example.com';
//$c->authenticate_hook['config'] = array(
// 'host' => 'www.tennaxia.net', //host name of your LDAP Server
// 'port' => '389', //port
/* For the initial bind to be anonymous leave bindDN and passDN
commented out */
// 'bindDN'=> 'cn=manager,cn=internal,dc=tennaxia,dc=net', //DN to bind to this server enabling to perform request
// 'passDN'=> 'xxxxxxxx', //Password of the previous bindDN to bind to this server enabling to perform request
// 'protocolVersion' => '3', //Version of LDAP protocol to use
// 'baseDNUsers'=> 'dc=tennaxia,dc=net', //where to look at valid user
// 'filterUsers' => 'objectClass=kolabInetOrgPerson', //filter which must validate a user according to RFC4515, i.e. surrounded by brackets
@ -193,6 +198,16 @@ $c->admin_email ='calendar-admin@example.com';
//include('drivers_ldap.php');
/**
* Authentication against PAM using the Squid helper script.
*/
//$c->authenticate_hook = array(
// 'call' => 'SQUID_PAM_check',
// 'config' => array( 'script' => '/usr/bin/pam_auth', 'email_base' => 'example.com' );
// );
//include('drivers_squid_pam.php');
/**
* The default locale will be "en_NZ";
* If you are in a non-English locale, you can set the default_locale
@ -225,6 +240,3 @@ $c->admin_email ='calendar-admin@example.com';
* of a VEVENT. The local (server) time zone will be used as a default.
*/
// $c->local_tzid;
?>

View File

@ -84,4 +84,3 @@
// $c->schema_patch
// $c->schema_version
?>

View File

@ -23,13 +23,11 @@
<item url="htdocs/caldav.php" uploadstatus="1" />
<item url="debian/" uploadstatus="1" />
<item url="debian/rules" uploadstatus="1" />
<item url="dba/rscds.sql" uploadstatus="1" />
<item url="htdocs/freebusy.php" uploadstatus="1" />
<item url="htdocs/index.php" uploadstatus="1" />
<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" uploadstatus="1" />
<item url="htdocs/rscds.css" uploadstatus="1" />
<item url="inc/interactive-page.php" uploadstatus="1" />
<item url="htdocs/users.php" uploadstatus="1" />
@ -236,6 +234,19 @@
<item url="inc/caldav-REPORT-calquery.php" uploadstatus="1" />
<item url="inc/caldav-REPORT-freebusy.php" uploadstatus="1" />
<item url="inc/caldav-REPORT-multiget.php" uploadstatus="1" />
<item url="inc/caldav-PUT-functions.php" uploadstatus="1" />
<item url="inc/HTTPAuthSession.php" uploadstatus="1" />
<item url="inc/caldav-REPORT-principal.php" uploadstatus="1" />
<item url="dba/patches/1.1.10.sql" uploadstatus="1" />
<item url="inc/auth-functions.php" uploadstatus="1" />
<item url="htdocs/tools.php" uploadstatus="1" />
<item url="inc/CalDAVPrincipal.php" uploadstatus="1" />
<item url="dba/patches/1.1.11.sql" uploadstatus="1" />
<item url="docs/website/clients/iCal-details.php" uploadstatus="1" />
<item url="inc/davical_configuration_missing.php" uploadstatus="1" />
<item url="dba/davical.sql" uploadstatus="1" />
<item url="inc/drivers_squid_pam.php" uploadstatus="1" />
<item url="dba/patches/1.1.11a.sql" uploadstatus="1" />
<item url="dba/patches/1.1.12.sql" uploadstatus="1" />
</project>
</webproject>

View File

@ -8,20 +8,65 @@ ADMINPW="${2}"
DBADIR="`dirname \"$0\"`"
testawldir() {
[ -f "${1}/dba/awl-tables.sql" ]
}
#
# Attempt to locate the AWL directory
AWLDIR="${DBADIR}/../../awl"
if ! testawldir "${AWLDIR}"; then
AWLDIR="/usr/share/awl"
if ! testawldir "${AWLDIR}"; then
AWLDIR="/usr/local/share/awl"
if ! testawldir "${AWLDIR}"; then
echo "Unable to find AWL libraries"
exit 1
fi
fi
fi
export AWL_DBAUSER=davical_dba
export AWL_APPUSER=davical_app
# Get the major version for PostgreSQL
export DBVERSION="`psql -qAt template1 -c "SELECT version();" | cut -f2 -d' ' | cut -f1-2 -d'.'`"
db_users() {
psql -qAt template1 -c "SELECT usename FROM pg_user;";
}
create_db_user() {
if ! db_users | grep "^${1}$" >/dev/null ; then
createuser --no-superuser --no-createdb --no-createrole "${1}"
fi
}
create_plpgsql_language() {
if ! psql -qAt template1 -c "SELECT lanname FROM pg_language;" | grep "^plpgsql$" >/dev/null; then
createlang plpgsql "${DBNAME}"
fi
}
create_db_user "${AWL_DBAUSER}"
create_db_user "${AWL_APPUSER}"
# FIXME: Need to check that the database was actually created.
if ! createdb -E UTF8 "${DBNAME}" -T template0 ; then
if ! createdb --encoding UTF8 "${DBNAME}" --template template0 --owner "${AWL_DBAUSER}"; then
echo "Unable to create database"
exit 1
fi
#
# This will fail if the language already exists, but it should not
# because we created from template0.
createlang plpgsql "${DBNAME}"
create_plpgsql_language
#
# FIXME: filter non-error output
psql -q -f "${DBADIR}/rscds.sql" "${DBNAME}" 2>&1 | egrep -v "(^CREATE |^GRANT|^BEGIN|^COMMIT| NOTICE: )"
# Load the AWL base tables and schema management tables
psql -q -f "${AWLDIR}/dba/awl-tables.sql" "${DBNAME}" 2>&1 | egrep -v "(^CREATE |^GRANT|^BEGIN|^COMMIT| NOTICE: )"
psql -q -f "${AWLDIR}/dba/schema-management.sql" "${DBNAME}" 2>&1 | egrep -v "(^CREATE |^GRANT|^BEGIN|^COMMIT| NOTICE: )"
#
# Load the DAViCal tables
psql -q -f "${DBADIR}/davical.sql" "${DBNAME}" 2>&1 | egrep -v "(^CREATE |^GRANT|^BEGIN|^COMMIT| NOTICE: )"
psql -q -f "${DBADIR}/caldav_functions.sql" "${DBNAME}"
@ -39,7 +84,7 @@ fi
if [ "$ADMINPW" = "" ] ; then
# OK. They didn't supply one, and pwgen didn't work, so we hack something
# together from /dev/random ...
ADMINPW="`dd if=/dev/urandom bs=512 count=1 2>/dev/null | tr -c -d "a-zA-HJ-NP-Y0-9" | cut -c2-9`"
ADMINPW="`dd if=/dev/urandom bs=512 count=1 2>/dev/null | tr -c -d "a-km-zA-HJ-NP-Y0-9" | cut -c2-9`"
fi
if [ "$ADMINPW" = "" ] ; then

View File

@ -1,13 +1,9 @@
-- Really Simple CalDAV Store - Database Schema
--
-- Use the usr, group and schema management stufffrom libawl-php
\i /usr/share/awl/dba/awl-tables.sql
\i /usr/share/awl/dba/schema-management.sql
-- The main event. Where we store the things the calendar throws at us.
CREATE TABLE caldav_data (
user_no INT references usr(user_no),
user_no INT references usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
dav_name TEXT,
dav_etag TEXT,
created TIMESTAMP WITH TIME ZONE,
@ -69,7 +65,7 @@ GRANT SELECT,INSERT,UPDATE,DELETE ON calendar_item TO general;
-- Something that can look like a filesystem hierarchy where we store stuff
CREATE TABLE collection (
user_no INT references usr(user_no),
user_no INT references usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
parent_container TEXT,
dav_name TEXT,
dav_etag TEXT,
@ -77,6 +73,7 @@ CREATE TABLE collection (
is_calendar BOOLEAN,
created TIMESTAMP WITH TIME ZONE,
modified TIMESTAMP WITH TIME ZONE,
public_events_only BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY ( user_no, dav_name )
);
@ -141,4 +138,4 @@ CREATE TABLE freebusy_ticket (
GRANT INSERT,SELECT,UPDATE,DELETE ON TABLE freebusy_ticket TO general;
SELECT new_db_revision(1,1,9, 'September' );
SELECT new_db_revision(1,1,11, 'November' );

20
dba/patches/1.1.10.sql Normal file
View File

@ -0,0 +1,20 @@
-- Sort out accessing calendar entries.
BEGIN;
SELECT check_db_revision(1,1,9);
-- Make sure that class is set to something, by default PUBLIC.
-- According to RFC2445, 4.8.1.3.
UPDATE calendar_item SET class = 'PUBLIC' WHERE class IS NULL;
-- Allow forcing all events in a calendar to be public
ALTER TABLE collection ADD COLUMN public_events_only BOOLEAN;
UPDATE collection SET public_events_only = FALSE;
ALTER TABLE collection ALTER public_events_only SET NOT NULL;
ALTER TABLE collection ALTER public_events_only SET DEFAULT FALSE;
SELECT new_db_revision(1,1,10, 'October' );
COMMIT;
ROLLBACK;

16
dba/patches/1.1.11.sql Normal file
View File

@ -0,0 +1,16 @@
-- Sort out accessing calendar entries.
BEGIN;
SELECT check_db_revision(1,1,10);
ALTER TABLE caldav_data DROP CONSTRAINT "$1";
ALTER TABLE caldav_data ADD CONSTRAINT "$1" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
ALTER TABLE collection DROP CONSTRAINT "$1";
ALTER TABLE collection ADD CONSTRAINT "$1" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
SELECT new_db_revision(1,1,11, 'November' );
COMMIT;
ROLLBACK;

17
dba/patches/1.1.11a.sql Normal file
View File

@ -0,0 +1,17 @@
-- Sort out accessing calendar entries.
-- This alternative patch file is the same in/out revision as 1.1.11 but it works with newer databases (8.x)
BEGIN;
SELECT check_db_revision(1,1,10);
ALTER TABLE caldav_data DROP CONSTRAINT "caldav_data_user_no_fkey";
ALTER TABLE caldav_data ADD CONSTRAINT "caldav_data_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
ALTER TABLE collection DROP CONSTRAINT "collection_user_no_fkey";
ALTER TABLE collection ADD CONSTRAINT "collection_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
SELECT new_db_revision(1,1,11, 'November' );
COMMIT;
ROLLBACK;

70
dba/patches/1.1.12.sql Normal file
View File

@ -0,0 +1,70 @@
-- Add a numeric foreign key link between caldav_data and calendar_item to
-- provide more efficient linking when the db has been initialised with a
-- non POSIX collation.
BEGIN;
SELECT check_db_revision(1,1,11);
-- Add a column to the collection table to allow us to mark collections
-- as publicly readable
ALTER TABLE collection ADD COLUMN publicly_readable BOOLEAN DEFAULT FALSE;
-- Add a numeric dav_id to link the caldav_data and calendar_item tables
ALTER TABLE caldav_data ADD COLUMN dav_id INT8;
ALTER TABLE calendar_item ADD COLUMN dav_id INT8;
CREATE SEQUENCE caldav_data_dav_id_seq;
GRANT SELECT,UPDATE ON caldav_data_dav_id_seq TO general;
CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS '
DECLARE
BEGIN
IF TG_OP = ''DELETE'' THEN
-- Just let the ON DELETE CASCADE handle this case
RETURN OLD;
END IF;
IF NEW.dav_id IS NULL THEN
NEW.dav_id = nextval(''caldav_data_dav_id_seq'');
END IF;
IF TG_OP = ''UPDATE'' THEN
IF OLD.dav_id = NEW.dav_id THEN
-- Nothing to do
RETURN NEW;
END IF;
END IF;
IF TG_RELNAME = ''caldav_data'' THEN
UPDATE calendar_item SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
ELSE
UPDATE caldav_data SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
END IF;
RETURN NEW;
END
' LANGUAGE 'plpgsql';
CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
CREATE TRIGGER calendar_item_sync_dav_id AFTER INSERT OR UPDATE ON calendar_item
FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
-- Now, using the trigger, magically assign dav_id to all rows in caldav_data and calendar_item
UPDATE caldav_data SET dav_id = dav_id;
ALTER TABLE caldav_data ALTER COLUMN dav_id SET DEFAULT nextval('caldav_data_dav_id_seq');
ALTER TABLE caldav_data ALTER COLUMN dav_id SET NOT NULL;
ALTER TABLE caldav_data ADD CONSTRAINT caldav_data_dav_id_key UNIQUE (dav_id);
ALTER TABLE calendar_item ADD CONSTRAINT calendar_item_dav_id_key UNIQUE (dav_id);
SELECT new_db_revision(1,1,12, 'December' );
COMMIT;
ROLLBACK;

View File

@ -46,18 +46,32 @@ my $current_revision = get_current_revision();
printf( "The database is currently at revision %d.%d.%d.\n", $current_revision->{'schema_major'}, $current_revision->{'schema_minor'}, $current_revision->{'schema_patch'} );
opendir( PATCHDIR, $patchdir ) or die "Can't open patch directory $patchdir";
my @patches = grep { /^([0-9]+)\.([0-9]+)\.([0-9]+)\.sql$/ } readdir(PATCHDIR);
my @patches = grep { /^([0-9]+)\.([0-9]+)\.([0-9]+)([a-z]?)\.sql$/ } readdir(PATCHDIR);
closedir(PATCHDIR);
@patches = sort { compare_revisions(revision_hash($a),revision_hash($b)); } @patches;
@patches = sort { compare_revisions(revision_hash($a),revision_hash($b), 1); } @patches;
my $applied = 0;
my $last_results = ''; # Will hold the last SQL result from applying a patch
for ( my $i=0; $i <= $#patches; $i++ ) {
printf( "Looking at patches[%d] (%s)\n", $i, $patches[$i]) if ( $debug );
if ( compare_revisions(revision_hash($patches[$i]),$current_revision) > 0 ) {
print "Applying patch $patches[$i]\n";
last unless( apply_patch( $patches[$i] ) );
print "Applying patch $patches[$i] ... ";
if ( !apply_patch( $patches[$i] ) ) {
# Skip to the end unless the next patch is an alternate for the same version.
if ( defined($patches[$i+1]) && compare_revisions(revision_hash($patches[$i]),revision_hash($patches[$i+1])) == 0 ) {
print "failed. Attempting next alternative.\n";
$applied--;
}
else {
print "failed!\n$last_results ==> No further patches will be attempted!\n";
last;
}
}
else {
print "succeeded.\n";
}
$applied++;
}
else {
@ -91,17 +105,20 @@ exit 0;
# which is of the form "1.2.3" or we have three parameters.
############################################################
sub revision_hash {
my $rev = +{};
my $rev = +{ 'schema_major', => 0, 'schema_minor' => 0, 'schema_patch' => 0, 'alternative' => '0' };
my $first = shift;
if ( $first =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)([^0-9]|$)/ ) {
return $rev unless ( defined($first) );
if ( $first =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)([a-z]?)([^0-9]|$)/ ) {
$rev->{'schema_major'} = $1;
$rev->{'schema_minor'} = $2;
$rev->{'schema_patch'} = $3;
$rev->{'alternative'} = $4;
}
else {
$rev->{'schema_major'} = $first;
$rev->{'schema_minor'} = shift;
$rev->{'schema_patch'} = shift;
$rev->{'alternative'} = '0';
}
return $rev;
}
@ -113,6 +130,7 @@ sub revision_hash {
sub compare_revisions {
my $a = shift;
my $b = shift;
my $test_alt = shift;
return -1 if ( $a->{'schema_major'} < $b->{'schema_major'} );
return 1 if ( $a->{'schema_major'} > $b->{'schema_major'} );
@ -123,6 +141,11 @@ sub compare_revisions {
return -1 if ( $a->{'schema_patch'} < $b->{'schema_patch'} );
return 1 if ( $a->{'schema_patch'} > $b->{'schema_patch'} );
if ( defined($test_alt) ) {
return -1 if ( $a->{'alternative'} lt $b->{'alternative'} );
return 1 if ( $a->{'alternative'} gt $b->{'alternative'} );
}
return 0;
}
@ -165,7 +188,7 @@ sub apply_patch {
$current_revision = get_current_revision();
if ( compare_revisions($current_revision,revision_hash($patch)) != 0 ) {
printf( "Failed to apply revision %s to the database!\n", $patch );
printf( "Failed to apply revision %s to the database!\n", $patch ) if ( $debug );
return 0;
}
return 1; # Success
@ -188,11 +211,10 @@ sub apply_sql_file {
$ENV{'PGPASS'} = $dbpass if ( $dbpass ne "" );
my $command = join ' ', @psql_opts;
my $results = `$command 2>&1 1>/dev/null`;
$last_results = `$command 2>&1 1>/dev/null`;
$results =~ s/^.*WARNING: there is no transaction in progress\s$//m;
print $results;
$last_results =~ s/^.*WARNING: there is no transaction in progress\s$//m;
$last_results =~ s/^.*NOTICE: //m;
}

27
debian/changelog vendored
View File

@ -1,3 +1,30 @@
rscds (0.9.2) unstable; urgency=low
* Add support for principal-url and calendar-home-set properties.
* All events should be PUBLIC unless CLASS specifies otherwise.
* Calendars can now be set such that all events are PUBLIC.
* Add support for automatically added relationships.
* Make some use of the improvements to the iCalendar class.
* Working with iCal 3.0 from Mac OS 10.5.
* Refactoring of driver code for LDAP and external AWL DB.
-- Andrew McMillan <debian@mcmillan.net.nz> Sun, 04 Nov 2007 23:31:10 +1300
rscds (0.9.1) unstable; urgency=low
* Reduce debug logging noise when debugging is iff
* When class is NULL we should consider it to be PUBLIC.
* Clean up some uninitialised variable warnings.
* Refactoring caldav-PUT to allow calling from a different code path.
* State how to make LDAP use an anonymous bind initially.
* Include any VTODO in GET for a collection.
* Minor permissions changes.
* Fix VTODO handling by time-range queries.
* Various fixes to LDAP authentication.
* Fix permissions for RW access.
-- Andrew McMillan <debian@mcmillan.net.nz> Thu, 25 Oct 2007 16:30:06 +1300
rscds (0.9.0) unstable; urgency=low
* Changes preparatory to renaming to DAViCal

View File

@ -43,6 +43,7 @@
sort($clients);
foreach( $clients AS $k => $v ) {
if ( $v == "Interoperability" ) continue;
if ( $v == "Other" ) continue;
$style = (strcmp($client_page,$v) == 0 ? ' class="selected"' : '' );
printf( '<p%s><a%s href="clients.php?client=%s">', $style, $style, urlencode($v) );
if ( isset($icons[$v]) ) {
@ -51,6 +52,9 @@
echo "$v</a></p>\n";
}
$style = ($client_page == "Other" ? ' class="selected"' : '' );
printf( '<p%s><a%s href="clients.php?client=Other">Other</a></p>', $style, $style );
include("inc/page-middle.php");
include("clients/".$details[$client_page]);

View File

@ -5,7 +5,7 @@ was little in the way of a repository available to test against until recently.<
<ol>
<li>Select "File" then "New" then "Calendar" from the menus.</li>
<li>Choose a type of "CalDAV", enter a name, and a URL such as <code>caldav://server.domain.name/caldav.php/username/home/</code>, enter your user name for RSCDS and click "OK".<img src="clients/Evolution-dialog1.png" /> <br />&nbsp;</li>
<li>Choose a type of "CalDAV", enter a name, and a URL such as <code>caldav://server.domain.name/caldav.php/username/home/</code>, enter your user name for DAViCal and click "OK".<img src="clients/Evolution-dialog1.png" /> <br />&nbsp;</li>
<li>You should now be prompted for a password for that username. Enter the password and your calendar should now show.</li>
</ol>
@ -15,11 +15,11 @@ restart. If you still have problems try doing that, but killing evolution-data-
</p>
<p>Sometimes Evolution writes error messages into the cache file, so if you have ongoing problems you may want to
take a look inside that.</p>
<p>There are some quirks with Evolution's handling of CalDAV too, so perhaps take a look at the following
bugs:</p>
<p>There are some quirks with Evolution's handling of CalDAV too, prior to 2.12.0, so perhaps take a look at the following
bugs (fixed in 2.12.0):</p>
<ul>
<li><a href="http://bugzilla.gnome.org/show_bug.cgi?id=355659">New appointments disappear for 1 minute, and then reappear</a></li>
<li><a href="http://bugzilla.gnome.org/show_bug.cgi?id=354855">Support Response with Relative URLs</a></li>
</ul>
<p>There may also be bugs in Evolution's handling of SSL with CalDAV - I couldn't get it to work reliably.</p>
<p>Hopefully those will be fixed before too long...</p>

View File

@ -1,5 +1,5 @@
<h1>Cross-client Interoperability Considerations</h1>
<p>If you intend to have users accessing the Really Simple CalDAV Store with more than one client
<p>If you intend to have users accessing the DAViCal CalDAV Server with more than one client
then you should attempt to structure the URLs which they use to access the system in the way
that Mulberry does it.</p>
<p>Basically, Mulberry breaks the URL into three parts:</p>

View File

@ -0,0 +1,23 @@
<h1>iCal</h1>
<p><a href="http://www.apple.com/macosx/features/300.html#ical">iCal</a>, from version 3.0 (released with OS 10.5) is generally well-behaved
and will discover your own calendars when configured. It will not allow you to manipulate other calendars on
the same server, however, unless you use different credentials to access them.</p>
<ol>
<li>Open the "Preferences" dialog.</li>
<li>Choose the "Accounts" tab</li>
<li>Click on the "+" and a new panel will appear.</li>
<li>Enter a "Description" for the account.</li>
<li>The "Username" and "Password" are the relevant ones for your CalDAV server.</li>
<li>Open the "Server Options" area and set your account URL to point to http://host.../caldav.php/username/.<img src="clients/iCal-dialog.png" /> <br /> &nbsp;</li>
<li>Click "Add" to confirm the new account</li>
<li>Your own calendars will be automatically discovered.</li>
<li>If you don't already have a calendar for your own user, go to the calendar view and long-click on the "+" will display a menu letting you create a new one.</li>
</ol>
<h2>Caveats</h2>
<p>DAViCal does not support the draft scheduling extensions to CalDAV, so you will not see the full functionality
of iCal.</p>
<p>iCal does not let you browse the calendar hierarchy to find other calendars you could view, so you will not
see the full functionality of DAViCal either.</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

View File

@ -11,7 +11,7 @@ echo $tags_to_be_closed;
<a href="http://validator.w3.org/check?uri=referer" class="flink">XHTML</a> | <a href="http://jigsaw.w3.org/css-validator/check/referer" class="flink">CSS</a>
</p>
<p class="right">
Copyright 2006 | Andrew McMillan
Copyright 2007 | Andrew McMillan
</p>
<p align="center">
<a href="http://andrew.mcmillan.net.nz/"><img src="http://sf-rscds.mcmillan.net.nz/clear-1.png" width="1" height="1" border="0" title="DAViCal CalDAV Server by Andrew McMillan" alt=" " /></a>

View File

@ -6,6 +6,9 @@
if ( isset($title) ) {
echo " - ". $title;
}
else {
echo " CalDAV Server";
}
?></title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
@ -17,7 +20,7 @@ if ( isset($title) ) {
echo $title;
}
else {
echo "DAViCal";
echo "DAViCal CalDAV Server";
}
?></div>
<div id="subTitle">A CalDAV Store</div>

View File

@ -40,9 +40,9 @@ few releases as we come to understand the particular problems people experience.
<p>In general DAViCal should not need significant maintenance to keep it operating.</p>
<p>Administrative functionality will be kept as simple as possible, within the target of supporting
organisations of up to several hundred staff.</p>
<p>This is called a <em>Store</em> rather than a <em>Server</em> because the server-side smarts are intended to be
minimised to support CalDAV only in a manner sufficient to inter-operate with clients, and with the focus primarily
on the storage of calendar resources.</p>
<p>The server-side smarts in DAViCal are intended to be fairly minimal in order to support CalDAV
only in a manner sufficient to inter-operate with clients, and with the focus primarily
on the storage of calendar resources.</p>
<h2>Web-based Administration</h2>
<p>General administration of the system should be through a web-based application.</p>
@ -51,7 +51,7 @@ maintainable through a web-based client, although the server should support the
works using the CalDAV protocol.</p>
<h1>Credits</h1>
<p>The Really Simple CalDAV Store was conceived and written by <a href="http://andrew.mcmillan.net.nz/">Andrew McMillan</a>.</p>
<p>DAViCal CalDAV Server was conceived and written by <a href="http://andrew.mcmillan.net.nz/">Andrew McMillan</a>.</p>
<p>Translations of the administration interface have been done by:</p>
<ul>
<li>Lorena Paoletti (Spanish)</li>
@ -62,7 +62,8 @@ works using the CalDAV protocol.</p>
</ul>
<p>Other contributors:</p>
<ul>
<li>Maxime Delorme (CSS for Administration Pages)</li>
<li>Maxime Delorme (CSS, LDAP, SyncML, French translations)</li>
<li>Andrew Ruthven (Various enhancements)</li>
</ul>
<h1>Your Name Here!</h1>

View File

@ -164,8 +164,11 @@ database on a different server, you should read the
<a href="http://www.postgresql.org/docs/8.1/interactive/client-authentication.html">PostgreSQL documentation on pg_hba.conf</a>
for the version you are using.</p>
<h1>Apache Configuration</h1>
<p>Once you have changed the pg_hba.conf file you will need to
reload or restart the PostgreSQL process for the change to come
into effect.</p>
<h1>Apache Configuration</h1>
<h2>Relative to an existing DocumentRoot</h2>
<p>You can create a symlink from an existing web root directory to the
@ -204,6 +207,7 @@ single virtual host.</p>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value error_reporting "E_ALL & ~E_NOTICE"
php_value default_charset "utf-8"
&lt;/VirtualHost>
</pre>

View File

@ -2,7 +2,7 @@
/**
* CalDAV Server - main program
*
* @package rscds
* @package davical
* @subpackage caldav
* @author Andrew McMillan <andrew@catalyst.net.nz>
* @copyright Catalyst .Net Ltd
@ -14,6 +14,18 @@ require_once("HTTPAuthSession.php");
$session = new HTTPAuthSession();
dbg_log_array( "headers", '_SERVER', $_SERVER, true );
/**
* From reading the "Scheduling Extensions to CalDAV" draft I don't think that we will
* be doing 'calendar-schedule' any time soon. The current spec is at:
* http://www.ietf.org/internet-drafts/draft-desruisseaux-caldav-sched-03.txt
*
* access-control is rfc3744, so we will say we do it, but I doubt if we do it
* in all (or even much of) it's glory really.
*/
$dav = "1, 2, access-control, calendar-access";
header( "DAV: $dav");
// header( "DAV: 1, 2, access-control, calendar-access, calendar-schedule");
require_once("CalDAVRequest.php");
$request = new CalDAVRequest();
@ -41,4 +53,3 @@ switch ( $request->method ) {
$request->DoResponse( 500, translate("The application program does not understand that request.") );
?>

View File

@ -1,4 +1,4 @@
/* CSS for browse pages in RSCDS */
/* CSS for browse pages in DAViCal */
tr.header th, td {
padding: 1px 4px;

View File

@ -5,13 +5,14 @@ $session->LoginRequired();
require_once("interactive-page.php");
$c->page_title = "Really Simple CalDAV Store - Configuration Help";
$c->page_title = "DAViCal CalDAV Server - Configuration Help";
include("page-header.php");
?>
<h1>Help</h1>
<p>For initial help you should visit the <a href="http://rscds.sourceforge.net/">RSCDS Home Page</a>. If you can't
find the answers there, then you should post your problem in the RSCDS forums on Sourceforge itself.</p>
<p>For initial help you should visit the <a href="http://rscds.sourceforge.net/">DAViCal Home Page</a>. If you can't
find the answers there, visit the #davical IRC channel on irc.oftc.net, send a question to the mailing list or
post your problem in the DAViCal forums on Sourceforge itself.</p>
<?php
include("page-footer.php");

View File

@ -1,9 +1,17 @@
<?php
require_once("../inc/always.php");
require_once("RSCDSSession.php");
if ( $_SERVER['REQUEST_METHOD'] != "GET" && $_SERVER['REQUEST_METHOD'] != "POST" ) {
/**
* If the request is not a GET or POST then they must really want caldav.php!
*/
include("./caldav.php");
exit; // Not that it should return from that!
}
include("../inc/always.php");
include("RSCDSSession.php");
$session->LoginRequired();
require_once("interactive-page.php");
include("interactive-page.php");
include("page-header.php");
echo <<<EOBODY
@ -48,16 +56,15 @@ and then the group is linked to each resource with the "Administers Resource" re
<li>Where a set of users link to a group, which then links to other users/resources, the access restrictions will apply as the lesser of their link to that group, or the link from the group. They will have no access to each other's calendars.</li>
</ul>
<h2>Configuring Calendar Clients for RSCDS</h2>
<p>The <a href="http://rscds.sourceforge.net/clients.php">RSCDS client setup page on sourceforge</a> have information on how
<h2>Configuring Calendar Clients for DAViCal</h2>
<p>The <a href="http://rscds.sourceforge.net/clients.php">DAViCal client setup page on sourceforge</a> have information on how
to configure Evolution, Sunbird, Lightning and Mulberry to use remotely hosted calendars.</p>
<p>The administrative interface has no facility for viewing or modifying calendar data.</p>
<h2>Configuring RSCDS</h2>
<h2>Configuring DAViCal</h2>
<p>If you can read this then things must be mostly working already.</p>
<p>The <a href="http://rscds.sourceforge.net/installation.php">RSCDS installation page</a> on sourceforge has
<p>The <a href="http://rscds.sourceforge.net/installation.php">DAViCal installation page</a> on sourceforge has
some further information on how to install and configure this application.</p>
<?php
include("page-footer.php");
?>

View File

@ -11,7 +11,7 @@ function Go( url ) {
*/
function LinkTo( tag, url ) {
tag.style.cursor = "pointer";
tag.setAttribute('onClick', "Go('" + url + "')");
tag.setAttribute('onClick', "Go('" + url.replace('&amp;','&') + "')");
tag.setAttribute('onMouseOut', "window.status='';return true;");
window.status = window.location.protocol + '//' + document.domain + url;
tag.setAttribute('onMouseover', "window.status = window.location.protocol + '//' + document.domain + '" + url + "';return true;");

View File

@ -2,7 +2,7 @@
/**
* Tools for manipulating calendars
*
* @package rscds
* @package davical
* @subpackage RSCDSSession
* @author Maxime Delorme <mdelorme@tennaxia.com>
* @copyright Maxime Delorme
@ -20,7 +20,7 @@ require_once("classBrowser.php");
if ( !$session->AllowedTo("Admin" ) )
exit;
if(isset($_POST['Sync_LDAP'])){
if( function_exists("sync_LDAP") && isset($_POST['Sync_LDAP'])){
sync_LDAP();
}
@ -38,7 +38,7 @@ class Tools {
function render(){
global $c;
echo $this->renderImportFromDirectory();
if($c->authenticate_hook['call'] == 'LDAP_check'){
if ( $c->authenticate_hook['call'] == 'LDAP_check' && function_exists("sync_LDAP") ) {
echo $this->renderSyncLDAP();
}
}

View File

@ -2,8 +2,8 @@
/**
* Display a list of all users
*
* @package rscds
* @subpackage RSCDSSession
* @package davical
* @subpackage Admin
* @author Andrew McMillan <andrew@catalyst.net.nz>
* @copyright Catalyst .Net Ltd
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2

163
inc/CalDAVPrincipal.php Normal file
View File

@ -0,0 +1,163 @@
<?php
/**
* An object representing a DAV 'Principal'
*
* @package davical
* @subpackage Principal
* @author Andrew McMillan <andrew@mcmillan.net.nz>
* @copyright Catalyst .Net Ltd
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
/**
* @var $_CalDAVPrincipalCache
* A global variable holding a cache of any DAV Principals which are
* read from the DB.
*/
$_CalDAVPrincipalCache = (object) array( 'p' => array(), 'u' => array() );
/**
* A class for things to do with a DAV Principal
*
* @package davical
*/
class CalDAVPrincipal
{
/**
* @var The home URL of the principal
*/
var $url;
/**
* @var RFC4791: Identifies the URL(s) of any WebDAV collections that contain
* calendar collections owned by the associated principal resource.
*/
var $calendar_home_set;
/**
* @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
* Inbox collection owned by the associated principal resource.
*/
var $schedule_inbox_url;
/**
* @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
* Outbox collection owned by the associated principal resource.
*/
var $schedule_outbox_url;
/**
* Constructor
* @param mixed $parameters If null, an empty Principal is created. If it
* is an integer then that ID is read (if possible). If it is
* an array then the Principal matching the supplied elements is read.
*
* @return boolean Whether we actually read data from the DB to initialise the record.
*/
function CalDAVPrincipal( $parameters = null ) {
global $session, $c;
if ( $parameters == null ) return false;
if ( is_int($parameters) ) {
dbg_error_log( "principal", "Principal: %d", $parameters );
$usr = getUserByID($parameters);
}
else if ( is_array($parameters) ) {
if ( isset($parameters['username']) ) {
$usr = getUserByName($parameters['username']);
}
else if ( isset($parameters['user_no']) ) {
$usr = getUserByID($parameters['user_no']);
}
else if ( isset($parameters['path']) ) {
dbg_error_log( "principal", "Finding Principal from path: '%s', options.allow_by_email: '%s'", $parameters['path'], $parameters['options']['allow_by_email'] );
if ( $username = $this->UsernameFromPath($parameters['path'], $parameters['options']) ) {
$usr = getUserByName($username);
if ( isset($parameters['options']['allow_by_email']) && is_object($usr) && preg_match( '#/(\S+@\S+[.]\S+)$#', $parameters['path']) ) {
$this->by_email = true;
}
}
}
else if ( isset($parameters['principal-property-search']) ) {
$usr = $this->PropertySearch($parameters['principal-property-search']);
}
}
if ( !isset($usr) || !is_object($usr) ) return false;
$this->InitialiseRecord($usr);
}
/**
* Initialise the Principal object from a $usr record from the DB.
* @param object $usr The usr record from the DB.
*/
function InitialiseRecord($usr) {
global $c;
foreach( $usr AS $k => $v ) {
$this->{$k} = $v;
}
$this->url = ConstructURL( "/".$this->username."/" );
// $this->url = ConstructURL( "/__uuids__/" . $this->username . "/" );
$this->calendar_home_set = ConstructURL( "/".$this->username."/" );
$this->user_address_set = array(
ConstructURL( "/".$this->username."/" ),
// ConstructURL( "/~".$this->username."/" ),
// ConstructURL( "/__uuids__/".$this->username."/" ),
);
$this->schedule_inbox_url = sprintf( "%s.in/", $this->calendar_home_set);
$this->schedule_outbox_url = sprintf( "%s.out/", $this->calendar_home_set);
$this->dropbox_url = sprintf( "%s.drop/", $this->calendar_home_set);
$this->notifications_url = sprintf( "%s.notify/", $this->calendar_home_set);
dbg_error_log( "principal", "User: %s (%d) URL: %s, Home: %s, By Email: %d", $this->username, $this->user_no, $this->url, $this->calendar_home_set, $this->by_email );
}
/**
* Work out the username, based on elements of the path.
* @param string $path The path to be used.
* @param array $options The request options, controlling whether e-mail paths are allowed.
*/
function UsernameFromPath( $path, $options = null ) {
global $session, $c;
if ( $path == '/' || $path == '' ) {
dbg_error_log( "principal", "No useful path split possible" );
return $session->username;
}
$path_split = explode('/', $path );
@dbg_error_log( "principal", "Path split into at least /// %s /// %s /// %s", $path_split[1], $path_split[2], $path_split[3] );
if ( substr($path,0,1) == '~' ) {
// URL is for a principal, by name
$username = substr($path_split[1],1);
$user = getUserByID($username);
$user_no = $user->user_no;
}
else {
$username = $path_split[1];
if ( isset($options['allow_by_email']) && preg_match( '#/(\S+@\S+[.]\S+)$#', $path, $matches) ) {
$email = $matches[1];
$qry = new PgQuery("SELECT user_no, username FROM usr WHERE email = ?;", $email );
if ( $qry->Exec("principal") && $user = $qry->Fetch() ) {
$user_no = $user->user_no;
$username = $user->username;
}
}
elseif( $user = getUserByName( $username, 'caldav') ) {
$user_no = $user->user_no;
}
}
return $username;
}
}

View File

@ -7,26 +7,53 @@
* - Utility functions which we can use to decide whether this
* is a permitted activity for this user.
*
* @package rscds
* @subpackage CalDAVRequest
* @package davical
* @subpackage Request
* @author Andrew McMillan <andrew@mcmillan.net.nz>
* @copyright Catalyst .Net Ltd
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
require_once("XMLElement.php");
require_once("CalDAVPrincipal.php");
define('DEPTH_INFINITY', 9999);
/**
* A class for collecting things to do with this request.
*
* @package rscds
* @package davical
*/
class CalDAVRequest
{
var $options;
/**
* The raw data sent along with the request
*/
var $raw_post;
/**
* The HTTP request method: PROPFIND, LOCK, REPORT, OPTIONS, etc...
*/
var $method;
/**
* The depth parameter from the request headers, coerced into a valid integer: 0, 1
* or DEPTH_INFINITY which is defined above. The default is set per various RFCs.
*/
var $depth;
/**
* The 'principal' (user/resource/...) which this request seeks to access
*/
var $principal;
/**
* The user agent making the request.
*/
var $user_agent;
/**
* Create a new CalDAVRequest object.
*/
@ -34,6 +61,7 @@ class CalDAVRequest
global $session, $c, $debugging;
$this->options = $options;
$this->principal = (object) array( 'username' => $session->username, 'user_no' => $session->user_no );
$this->raw_post = file_get_contents ( 'php://input');
@ -42,6 +70,8 @@ class CalDAVRequest
}
$this->method = $_SERVER['REQUEST_METHOD'];
$this->user_agent = ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "Probably Mulberry"));
/**
* A variety of requests may set the "Depth" header to control recursion
*/
@ -117,6 +147,7 @@ class CalDAVRequest
* the minimum privileges returned from that analysis.
*/
$this->path = $_SERVER['PATH_INFO'];
if ( $this->path == null || $this->path == '' ) $this->path = '/';
// dbg_error_log( "caldav", "Sanitising path '%s'", $this->path );
$bad_chars_regex = '/[\\^\\[\\(\\\\]/';
if ( preg_match( $bad_chars_regex, $this->path ) ) {
@ -138,10 +169,17 @@ class CalDAVRequest
}
}
$this->user_no = $session->user_no;
$this->username = $session->username;
/**
* Extract the user whom we are accessing
*/
$this->UserFromPath();
$this->principal = new CalDAVPrincipal( array( "path" => $this->path, "options" => $this->options ) );
if ( isset($this->principal->user_no) ) $this->user_no = $this->principal->user_no;
if ( isset($this->principal->username)) $this->username = $this->principal->username;
if ( isset($this->principal->by_email)) $this->by_email = true;
/**
* Evaluate our permissions for accessing the target
@ -200,7 +238,8 @@ class CalDAVRequest
$this->user_no = $user->user_no;
}
}
elseif( $user = getUserByName($this->username,'caldav',__LINE__,__FILE__)){
elseif( $user = getUserByName($this->username,'caldav',__LINE__,__FILE__)) {
$this->principal = $user;
$this->user_no = $user->user_no;
}
}
@ -481,9 +520,14 @@ class CalDAVRequest
break;
case 'create':
return isset($this->permissions['write']) || isset($this->permissions['bind']);
break;
case 'mkcalendar':
case 'mkcol':
return isset($this->permissions['write']) || isset($this->permissions['bind']);
if ( !isset($this->permissions['write']) || !isset($this->permissions['bind']) ) return false;
if ( $this->is_principal ) return false;
if ( $this->path == '/' ) return false;
break;
case 'read':
@ -566,4 +610,3 @@ class CalDAVRequest
}
}
?>

View File

@ -50,7 +50,7 @@ class HTTPAuthSession {
function HTTPAuthSession() {
global $c;
if ( $c->http_auth_mode == "Digest" ) {
if ( isset($c->http_auth_mode) && $c->http_auth_mode == "Digest" ) {
$this->DigestAuthSession();
}
else {

View File

@ -282,23 +282,25 @@ class iCalDate {
function AddDuration( $duration ) {
list( $sign, $days, $time ) = preg_split( '/[PT]/', $duration );
$sign = ( $sign == "-" ? -1 : 1);
dbg_error_log( "RRule", " Adding duration to '%s' of sign: %d, days: %s, time: %s", $this->_text, $sign, $days, $time );
if ( preg_match( '/(\d)+(D|W)/', $days, $matches ) ) {
$days = intval($matches[1]);
if ( $matches[2] == 'W' ) $days *= 7;
$this->AddDays( $days * $sign );
}
if ( preg_match( '/(\d)+(H)/', $time, $matches ) ) $hh = $matches[1];
if ( preg_match( '/(\d)+(M)/', $time, $matches ) ) $mi = $matches[1];
if ( preg_match( '/(\d)+(S)/', $time, $matches ) ) $ss = $matches[1];
if ( preg_match( '/(\d+)(H)/', $time, $matches ) ) $hh = $matches[1];
if ( preg_match( '/(\d+)(M)/', $time, $matches ) ) $mi = $matches[1];
if ( preg_match( '/(\d+)(S)/', $time, $matches ) ) $ss = $matches[1];
dbg_error_log( "RRule", " Adding %02d:%02d:%02d * %d to %02d:%02d:%02d", $hh, $mi, $ss, $sign, $this->_hh, $this->_mi, $this->_ss );
$this->_hh += ($hh * $sign);
$this->_mi += ($mi * $sign);
$this->_ss += ($ss * $sign);
if ( $this->_ss < 0 ) { $this->_mi -= (intval(abs($this->ss/60))+1); $this->_ss += ((intval(abs($this->mi/60))+1) * 60); }
if ( $this->_ss > 59) { $this->_mi += (intval(abs($this->ss/60))+1); $this->_ss -= ((intval(abs($this->mi/60))+1) * 60); }
if ( $this->_mi < 0 ) { $this->_hh -= (intval(abs($this->mi/60))+1); $this->_mi += ((intval(abs($this->mi/60))+1) * 60); }
if ( $this->_mi > 59) { $this->_hh += (intval(abs($this->mi/60))+1); $this->_mi -= ((intval(abs($this->mi/60))+1) * 60); }
if ( $this->_ss < 0 ) { $this->_mi -= (intval(abs($this->_ss/60))+1); $this->_ss += ((intval(abs($this->_mi/60))+1) * 60); }
if ( $this->_ss > 59) { $this->_mi += (intval(abs($this->_ss/60))+1); $this->_ss -= ((intval(abs($this->_mi/60))+1) * 60); }
if ( $this->_mi < 0 ) { $this->_hh -= (intval(abs($this->_mi/60))+1); $this->_mi += ((intval(abs($this->_mi/60))+1) * 60); }
if ( $this->_mi > 59) { $this->_hh += (intval(abs($this->_mi/60))+1); $this->_mi -= ((intval(abs($this->_mi/60))+1) * 60); }
if ( $this->_hh < 0 ) { $this->AddDays( -1 * (intval(abs($this->_hh/24))+1) ); $this->_hh += ((intval(abs($this->_hh/24))+1)*24); }
if ( $this->_hh > 23) { $this->AddDays( (intval(abs($this->_hh/24))+1) ); $this->_hh -= ((intval(abs($this->_hh/24))+1)*24); }

View File

@ -11,6 +11,7 @@
require_once("User.php");
require_once("classBrowser.php");
require_once("auth-functions.php");
$c->stylesheets[] = "$c->base_url/css/browse.css";
$c->scripts[] = "$c->base_url/js/browse.js";
@ -306,24 +307,11 @@ EOSQL;
if ( parent::Write() ) {
if ( $this->WriteType == 'insert' ) {
if ( isset($c->home_calendar_name) && strlen($c->home_calendar_name) > 0 ) {
$parent_path = "/".$this->Get('username')."/";
$calendar_path = $parent_path . $c->home_calendar_name."/";
$dav_etag = md5($this->user_no . $calendar_path);
$sql = "INSERT INTO collection (user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, ";
$sql .= "created, modified) VALUES( ?, ?, ?, ?, ?, true, current_timestamp, current_timestamp );";
$qry = new PgQuery( $sql, $this->user_no, $parent_path, $calendar_path, $dav_etag, $this->Get('fullname') );
if ( $qry->Exec() ) {
$c->messages[] = i18n("Home calendar added.");
dbg_error_log("User",":Write: Created user's home calendar at '%s'", $calendar_path );
}
else {
$c->messages[] = i18n("There was an error writing to the database.");
return false;
}
}
$username = $this->Get('username');
CreateHomeCalendar($username);
CreateDefaultRelationships($username);
}
if ( $this->AllowedTo("Admin") && isset($_POST['relate_to']) && isset($_POST['relate_as']) && isset($_POST['submit']) && $_POST['submit'] == htmlspecialchars(translate('Add Relationship')) ) {
if ( $this->AllowedTo("Admin") && isset($_POST['relate_to']) && $_POST['relate_to'] != '' && isset($_POST['relate_as']) && $_POST['relate_as'] != '' && isset($_POST['submit']) && $_POST['submit'] == htmlspecialchars(translate('Add Relationship')) ) {
dbg_error_log("User",":Write: Adding relationship as %d to %d", $_POST['relate_as'], isset($_POST['relate_to'] ) );
$qry = new PgQuery("INSERT INTO relationship (from_user, to_user, rt_id ) VALUES( $this->user_no, ?, ? )", $_POST['relate_to'], $_POST['relate_as'] );
if ( $qry->Exec() ) {
@ -338,6 +326,6 @@ EOSQL;
}
return false;
}
}
?>

View File

@ -11,13 +11,14 @@ unset($c);
// Default some of the configurable values
$c->sysabbr = 'davical';
$c->admin_email = 'andrew@catalyst.net.nz';
$c->admin_email = 'admin@davical.example.com';
$c->system_name = "DAViCal CalDAV Server";
$c->domain_name = $_SERVER['SERVER_NAME'];
$c->save_time_zone_defs = true;
$c->collections_always_exist = true;
$c->home_calendar_name = 'home';
$c->enable_row_linking = true;
$c->http_auth_mode = 'Basic';
// $c->default_locale = array('es_MX', 'es_MX.UTF-8', 'es');
// $c->local_tzid = 'Pacific/Auckland'; // Perhaps we should read from /etc/timezone - I wonder how standard that is?
$c->default_locale = "en_NZ";
@ -48,9 +49,7 @@ $c->protocol_server_port_script = sprintf( "%s://%s%s%s", (isset($_SERVER['HTTPS
? ''
: ':'.$_SERVER['SERVER_PORT']
),
$_SERVER['SCRIPT_NAME'] );
@dbg_error_log( "LOG", "==========> method =%s= =%s= =%s= =%s= =%s=", $_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']) );
init_gettext( 'rscds', '../locale' );
@ -64,11 +63,17 @@ else if ( file_exists("../config/config.php") ) {
include_once("../config/config.php");
}
else {
include_once("rscds_configuration_missing.php");
include_once("davical_configuration_missing.php");
exit;
}
if ( !isset($c->page_title) ) $c->page_title = $c->system_name;
if ( count($c->dbg) > 0 ) {
// Only log this if debugging of some sort is turned on, somewhere
@dbg_error_log( "LOG", "==========> method =%s= =%s= =%s= =%s= =%s=",
$_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
}
/**
* Now that we have loaded the configuration file we can switch to a
* default site locale. This may be overridden by each user.
@ -80,15 +85,15 @@ awl_set_locale($c->default_locale);
*
*/
$c->code_version = 0;
$c->version_string = '0.9.0'; // The actual version # is replaced into that during the build /release process
$c->version_string = '0.9.2'; // The actual version # is replaced into that during the build /release process
if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
$c->code_major = $matches[1];
$c->code_minor = $matches[1];
$c->code_patch = $matches[1];
$c->code_minor = $matches[2];
$c->code_patch = $matches[3];
$c->code_version = (($c->code_major * 1000) + $c->code_minor).".".$c->code_patch;
}
dbg_error_log("caldav", "Version %s (%d.%d.%d) == %s", $c->code_pkgver, $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
header( sprintf("Server: %s/%d.%d", $c->code_pkgver, $c->code_major, $c->code_minor) );
dbg_error_log("caldav", "Version (%d.%d.%d) == %s", $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
header( sprintf("Server: %d.%d", $c->code_major, $c->code_minor) );
/**
* Force the domain name to what was in the configuration file
@ -106,15 +111,45 @@ if ( $qry->Exec("always") && $row = $qry->Fetch() ) {
$c->schema_patch = $row->schema_patch;
}
$_known_users = array();
function getUserByName( $username ) {
$_known_users_name = array();
$_known_users_id = array();
/**
* Return a user record identified by a username, caching it for any subsequent lookup
* @param string $username The username of the record to retrieve
* @param boolean $use_cache Whether or not to use the cache (default: yes)
*/
function getUserByName( $username, $use_cache = true ) {
// Provide some basic caching in case this ends up being overused.
if ( isset( $_known_users[$username] ) ) return $_known_users[$username];
if ( $use_cache && isset( $_known_users_name[$username] ) ) return $_known_users_name[$username];
$qry = new PgQuery( "SELECT * FROM usr WHERE lower(username) = lower(?) ", $username );
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
$_known_users[$username] = $qry->Fetch();
return $_known_users[$username];
$_known_users_name[$username] = $qry->Fetch();
$id = $_known_users_name[$username]->user_no;
$_known_users_id[$id] = $_known_users_name[$username];
return $_known_users_name[$username];
}
return false;
}
/**
* Return a user record identified by a user_no, caching it for any subsequent lookup
* @param int $user_no The ID of the record to retrieve
* @param boolean $use_cache Whether or not to use the cache (default: yes)
*/
function getUserByID( $user_no, $use_cache = true ) {
// Provide some basic caching in case this ends up being overused.
if ( $use_cache && isset( $_known_users_id[$user_no] ) ) return $_known_users_id[$user_no];
$qry = new PgQuery( "SELECT * FROM usr WHERE user_no = ? ", intval($user_no) );
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
$_known_users_id[$user_no] = $qry->Fetch();
$name = $_known_users_id[$user_no]->username;
$_known_users_name[$name] = $_known_users_id[$user_no];
return $_known_users_id[$user_no];
}
return false;
@ -178,4 +213,24 @@ function getStatusMessage($status) {
}
/**
* Construct a URL from the supplied dav_name
* @param string $partial_path The part of the path after the script name
*/
function ConstructURL( $partial_path ) {
global $c;
if ( ! isset($c->_url_script_path) ) {
$c->_url_script_path = (preg_match('#/$#', $c->protocol_server_port_script) ? 'caldav.php' : '');
$c->_url_script_path = $c->protocol_server_port_script . $c->_url_script_path;
}
$url = $c->_url_script_path . $partial_path;
$url = preg_replace( '#^(https?://.+)//#', '$1/', $url ); // Ensure we don't double any '/'
$url = preg_replace('#^https?://[^/]+#', '', $url );
return $url;
}
?>

View File

@ -11,13 +11,14 @@ unset($c);
// Default some of the configurable values
$c->sysabbr = 'davical';
$c->admin_email = 'andrew@catalyst.net.nz';
$c->admin_email = 'admin@davical.example.com';
$c->system_name = "DAViCal CalDAV Server";
$c->domain_name = $_SERVER['SERVER_NAME'];
$c->save_time_zone_defs = true;
$c->collections_always_exist = true;
$c->home_calendar_name = 'home';
$c->enable_row_linking = true;
$c->http_auth_mode = 'Basic';
// $c->default_locale = array('es_MX', 'es_MX.UTF-8', 'es');
// $c->local_tzid = 'Pacific/Auckland'; // Perhaps we should read from /etc/timezone - I wonder how standard that is?
$c->default_locale = "en_NZ";
@ -48,9 +49,7 @@ $c->protocol_server_port_script = sprintf( "%s://%s%s%s", (isset($_SERVER['HTTPS
? ''
: ':'.$_SERVER['SERVER_PORT']
),
$_SERVER['SCRIPT_NAME'] );
@dbg_error_log( "LOG", "==========> method =%s= =%s= =%s= =%s= =%s=", $_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']) );
init_gettext( 'rscds', '../locale' );
@ -64,11 +63,17 @@ else if ( file_exists("../config/config.php") ) {
include_once("../config/config.php");
}
else {
include_once("rscds_configuration_missing.php");
include_once("davical_configuration_missing.php");
exit;
}
if ( !isset($c->page_title) ) $c->page_title = $c->system_name;
if ( count($c->dbg) > 0 ) {
// Only log this if debugging of some sort is turned on, somewhere
@dbg_error_log( "LOG", "==========> method =%s= =%s= =%s= =%s= =%s=",
$_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
}
/**
* Now that we have loaded the configuration file we can switch to a
* default site locale. This may be overridden by each user.
@ -83,12 +88,12 @@ $c->code_version = 0;
$c->version_string = '0.7.0~rc3'; // The actual version # is replaced into that during the build /release process
if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
$c->code_major = $matches[1];
$c->code_minor = $matches[1];
$c->code_patch = $matches[1];
$c->code_minor = $matches[2];
$c->code_patch = $matches[3];
$c->code_version = (($c->code_major * 1000) + $c->code_minor).".".$c->code_patch;
}
dbg_error_log("caldav", "Version %s (%d.%d.%d) == %s", $c->code_pkgver, $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
header( sprintf("Server: %s/%d.%d", $c->code_pkgver, $c->code_major, $c->code_minor) );
dbg_error_log("caldav", "Version (%d.%d.%d) == %s", $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
header( sprintf("Server: %d.%d", $c->code_major, $c->code_minor) );
/**
* Force the domain name to what was in the configuration file
@ -106,15 +111,45 @@ if ( $qry->Exec("always") && $row = $qry->Fetch() ) {
$c->schema_patch = $row->schema_patch;
}
$_known_users = array();
function getUserByName( $username ) {
$_known_users_name = array();
$_known_users_id = array();
/**
* Return a user record identified by a username, caching it for any subsequent lookup
* @param string $username The username of the record to retrieve
* @param boolean $use_cache Whether or not to use the cache (default: yes)
*/
function getUserByName( $username, $use_cache = true ) {
// Provide some basic caching in case this ends up being overused.
if ( isset( $_known_users[$username] ) ) return $_known_users[$username];
if ( $use_cache && isset( $_known_users_name[$username] ) ) return $_known_users_name[$username];
$qry = new PgQuery( "SELECT * FROM usr WHERE lower(username) = lower(?) ", $username );
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
$_known_users[$username] = $qry->Fetch();
return $_known_users[$username];
$_known_users_name[$username] = $qry->Fetch();
$id = $_known_users_name[$username]->user_no;
$_known_users_id[$id] = $_known_users_name[$username];
return $_known_users_name[$username];
}
return false;
}
/**
* Return a user record identified by a user_no, caching it for any subsequent lookup
* @param int $user_no The ID of the record to retrieve
* @param boolean $use_cache Whether or not to use the cache (default: yes)
*/
function getUserByID( $user_no, $use_cache = true ) {
// Provide some basic caching in case this ends up being overused.
if ( $use_cache && isset( $_known_users_id[$user_no] ) ) return $_known_users_id[$user_no];
$qry = new PgQuery( "SELECT * FROM usr WHERE user_no = ? ", intval($user_no) );
if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows == 1 ) {
$_known_users_id[$user_no] = $qry->Fetch();
$name = $_known_users_id[$user_no]->username;
$_known_users_name[$name] = $_known_users_id[$user_no];
return $_known_users_id[$user_no];
}
return false;
@ -178,4 +213,21 @@ function getStatusMessage($status) {
}
?>
/**
* Construct a URL from the supplied dav_name
* @param string $partial_path The part of the path after the script name
*/
function ConstructURL( $partial_path ) {
global $c;
if ( ! isset($c->_url_script_path) ) {
$c->_url_script_path = (preg_match('#/$#', $c->protocol_server_port_script) ? 'caldav.php' : '');
$c->_url_script_path = $c->protocol_server_port_script . $c->_url_script_path;
}
$url = $c->_url_script_path . $partial_path;
$url = preg_replace( '#^(https?://.+)//#', '$1/', $url ); // Ensure we don't double any '/'
$url = preg_replace('#^https?://[^/]+#', '', $url );
return $url;
}

179
inc/auth-functions.php Normal file
View File

@ -0,0 +1,179 @@
<?php
/**
* The authentication handling plugins can be used by the Session class to
* provide authentication.
*
* Each authenticate hook needs to:
* - Accept a username / password
* - Confirm the username / password are correct
* - Create (or update) a 'usr' record in our database
* - Return the 'usr' record as an object
* - Return === false when authentication fails
*
* It can expect that:
* - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
*
* In order to be called:
* - This file should be included
* - $c->authenticate_hook['call'] should be set to the name of the plugin
* - $c->authenticate_hook['config'] should be set up with any configuration data for the plugin
*
* @package davical
* @subpackage authentication
* @author Andrew McMillan <andrew@catalyst.net.nz>
* @copyright Catalyst IT Ltd
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
require_once("AWLUtilities.php");
require_once("DataUpdate.php");
/**
* Create a default home calendar for the user.
* @param string $username The username of the user we are creating relationships for.
*/
function CreateHomeCalendar( $username ) {
global $session, $c;
if ( ! isset($c->home_calendar_name) || strlen($c->home_calendar_name) == 0 ) return true;
$usr = getUserByName( $username );
$parent_path = "/".$username."/";
$calendar_path = $parent_path . $c->home_calendar_name."/";
$dav_etag = md5($usr->user_no . $calendar_path);
$sql = "INSERT INTO collection (user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, ";
$sql .= "created, modified) VALUES( ?, ?, ?, ?, ?, true, current_timestamp, current_timestamp );";
$qry = new PgQuery( $sql, $usr->user_no, $parent_path, $calendar_path, $dav_etag, $usr->fullname);
if ( $qry->Exec() ) {
$c->messages[] = i18n("Home calendar added.");
dbg_error_log("User",":Write: Created user's home calendar at '%s'", $calendar_path );
}
else {
$c->messages[] = i18n("There was an error writing to the database.");
return false;
}
return true;
}
/**
* Create default relationships
* @param string $username The username of the user we are creating relationships for.
*/
function CreateDefaultRelationships( $username ) {
global $session, $c;
if ( ! isset($c->default_relationships) || !is_array($c->default_relationships) || count($c->default_relationships) == 0 ) return false;
$usr = getUserByName( $username );
$sql = "";
foreach( $c->default_relationships AS $to_user => $permission ) {
$sql .= "INSERT INTO relationship (from_user, to_user, rt_id) ";
$sql .= "VALUES( $usr->user_no, $to_user, (select rt_id from relationship_type where confers = '$permission' order by rt_id limit 1) );";
}
$qry = new PgQuery( $sql );
if ( $qry->Exec() ) {
$c->messages[] = i18n("Default relationships added.");
dbg_error_log("User",":Write: Added default relationships" );
}
else {
$c->messages[] = i18n("There was an error writing to the database.");
return false;
}
return true;
}
/**
* Update the local cache of the remote user details
* @param object $usr The user details we read from the remote.
*/
function UpdateUserFromExternal( &$usr ) {
/**
* When we're doing the create we will usually need to generate a user number
*/
if ( !isset($usr->user_no) || intval($usr->user_no) == 0 ) {
$qry = new PgQuery( "SELECT nextval('usr_user_no_seq');" );
$qry->Exec('Login',__LINE,__FILE__);
$sequence_value = $qry->Fetch(true); // Fetch as an array
$usr->user_no = $sequence_value[0];
}
$qry = new PgQuery("SELECT * FROM usr WHERE user_no = $usr->user_no;" );
if ( $qry->Exec('Login',__LINE,__FILE__) && $qry->rows == 1 )
$type = "UPDATE";
else
$type = "INSERT";
$qry = new PgQuery( sql_from_object( $usr, $type, 'usr', "WHERE user_no=$usr->user_no" ) );
$qry->Exec('Login',__LINE,__FILE__);
/**
* We disallow login by inactive users _after_ we have updated the local copy
*/
if ( isset($usr->active) && $usr->active == 'f' ) return false;
if ( $type == 'INSERT' ) {
CreateHomeCalendar($usr->username);
CreateDefaultRelationships($usr->username);
}
}
/**
* Authenticate against a different PostgreSQL database which contains a usr table in
* the AWL format.
*
* Use this as in the following example config snippet:
*
* require_once('auth-functions.php');
* $c->authenticate_hook = array(
* 'call' => 'AuthExternalAwl',
* 'config' => array(
* // A PgSQL database connection string for the database containing user records
* 'connection' => 'dbname=wrms host=otherhost port=5433 user=general',
* // Which columns should be fetched from the database
* 'columns' => "user_no, active, email_ok, joined, last_update AS updated, last_used, username, password, fullname, email",
* // a WHERE clause to limit the records returned.
* 'where' => "active AND org_code=7"
* )
* );
*
*/
function AuthExternalAWL( $username, $password ) {
global $c;
$authconn = pg_Connect($c->authenticate_hook['config']['connection']);
if ( ! $authconn ) {
echo <<<EOERRMSG
<html><head><title>Database Connection Failure</title></head><body>
<h1>Database Error</h1>
<h3>Could not connect to PostgreSQL database</h3>
</body>
</html>
EOERRMSG;
exit(1);
}
if ( isset($c->authenticate_hook['config']['columns']) )
$cols = $c->authenticate_hook['config']['columns'];
else
$cols = "*";
if ( isset($c->authenticate_hook['config']['where']) )
$andwhere = " AND ".$c->authenticate_hook['config']['where'];
else
$andwhere = "";
$qry = new PgQuery("SELECT $cols FROM usr WHERE lower(username) = ? $andwhere", strtolower($username) );
$qry->SetConnection($authconn);
if ( $qry->Exec('Login',__LINE,__FILE__) && $qry->rows == 1 ) {
$usr = $qry->Fetch();
if ( session_validate_password( $password, $usr->password ) ) {
UpdateUserFromExternal();
return $usr;
}
}
return false;
}

View File

@ -15,7 +15,7 @@ if ( ! $request->AllowedTo('read') ) {
}
$privacy_clause = "";
if ( ! $request->AllowedTo('all') ) {
$privacy_clause = "AND calendar_item.class != 'PRIVATE'";
$privacy_clause = "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
}
if ( $request->IsCollection() ) {
@ -45,6 +45,10 @@ else if ( $qry->rows > 1 ) {
*/
include_once("iCalendar.php");
$response = iCalendar::iCalHeader();
$collqry = new PgQuery( "SELECT * FROM collection WHERE collection.user_no = ? AND collection.dav_name = ?;", $request->user_no, $request->path);
if ( $collqry->Exec("GET") && $collection = $collqry->Fetch() ) {
$response .= "X-WR-CALNAME:$collection->dav_displayname\r\n";
}
$timezones = array();
while( $event = $qry->Fetch() ) {
$ical = new iCalendar( array( "icalendar" => $event->caldav_data ) );
@ -57,13 +61,27 @@ else if ( $qry->rows > 1 ) {
// the user is not admin / owner of this calendarlooking at his calendar and can not admin the other cal
if ( $event->class == 'CONFIDENTIAL' ) {
// if the event is confidential we fake one that just says "Busy"
$displayname = translate("Busy");
$ical->Put( 'SUMMARY', $displayname );
$response .= $ical->Render( false, $event->caldav_type, $ical->DefaultPropertyList() );
$confidential = new iCalendar( array(
'SUMMARY' => translate('Busy'), 'CLASS' => 'CONFIDENTIAL',
'DTSTART' => $ical->Get('DTSTART'),
'RRULE' => $ical->Get('RRULE')
) );
$duration = $ical->Get('DURATION');
if ( isset($duration) && $duration != "" ) {
$confidential->Set('DURATION', $duration );
}
else {
$confidential->Set('DTEND', $ical->Get('DTEND') );
}
$response .= $confidential->Render( false, $event->caldav_type );
}
elseif ( $c->hide_alarm ) {
// Otherwise we hide the alarms (if configured to)
$response .= $ical->Render( false, $event->caldav_type, $ical->DefaultPropertyList() );
$ical->component->ClearComponents('VALARM');
$response .= $ical->render(true, $event->caldav_type );
}
else {
$response .= $ical->Render( false, $event->caldav_type );
}
}
else {
@ -81,4 +99,3 @@ else {
$request->DoResponse( 500, translate("Database Error") );
}
?>

View File

@ -15,6 +15,13 @@ if ( ! $request->AllowedTo('mkcalendar') ) {
}
$displayname = $request->path;
// Enforce trailling '/' on collection name
if ( ! preg_match( '#/$#', $request->path ) ) {
dbg_error_log( "MKCALENDAR", "Add trailling '/' to '%s'", $request->path);
$request->path .= '/';
}
$parent_container = '/';
if ( preg_match( '#^(.*/)([^/]+)(/)?$#', $request->path, $matches ) ) {
$parent_container = $matches[1];
@ -42,13 +49,26 @@ if ( isset($request->xml_tags) ) {
case 'DAV::DISPLAYNAME':
$displayname = $content;
/**
* TODO: This is definitely a bug in SOHO Organizer and we probably should respond
* with an error, rather than silently doing what they *seem* to want us to do.
*/
if ( preg_match( '/^SOHO.Organizer.6\./', $_SERVER['HTTP_USER_AGENT'] ) ) {
dbg_error_log( "MKCALENDAR", "Displayname is '/' to '%s'", $request->path);
$parent_container = $request->path;
$request->path .= $content . '/';
}
$success[$tag] = 1;
break;
case 'DAV::RESOURCETYPE':
/**
* Any value for resourcetype is ignored
*/
case 'URN:IETF:PARAMS:XML:NS:CALDAV:SUPPORTED-CALENDAR-COMPONENT-SET': /** Ignored, since we will support all component types */
case 'URN:IETF:PARAMS:XML:NS:CALDAV:SUPPORTED-CALENDAR-DATA': /** Ignored, since we will support iCalendar 2.0 */
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA': /** Ignored, since we will support iCalendar 2.0 */
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-RESOURCE-SIZE': /** Ignored, since we will support arbitrary size */
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MIN-DATE-TIME': /** Ignored, since we will support arbitrary time */
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-DATE-TIME': /** Ignored, since we will support arbitrary time */
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-INSTANCES': /** Ignored, since we will support arbitrary instances */
case 'DAV::RESOURCETYPE': /** Any value for resourcetype is ignored */
$success[$tag] = 1;
break;
@ -109,7 +129,7 @@ if ( $qry->rows != 0 ) {
$request->DoResponse( 405, translate("A collection already exists at that location.") );
}
$sql = "INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified ) VALUES( ?, ?, ?, ?, ?, ?, current_timestamp, current_timestamp );";
$sql = "BEGIN; INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified ) VALUES( ?, ?, ?, ?, ?, ?, current_timestamp, current_timestamp ); $propertysql; COMMIT;";
$qry = new PgQuery( $sql, $request->user_no, $parent_container, $request->path, md5($request->user_no. $request->path), $displayname, ($request->method == 'MKCALENDAR') );
if ( $qry->Exec("MKCALENDAR",__LINE__,__FILE__) ) {

View File

@ -11,7 +11,7 @@
dbg_error_log("OPTIONS", "method handler");
if ( ! $request->AllowedTo('read') ) {
$request->DoResponse( 403, translate("You may not access that calendar") );
$request->DoResponse( 403, translate("You may not access that collection") );
}
$exists = false;
@ -55,21 +55,13 @@ if ( isset($c->override_allowed_methods) )
$allowed = $c->override_allowed_methods;
else {
$allowed = "OPTIONS, GET, HEAD, PUT, DELETE, PROPFIND, MKCOL, MKCALENDAR, LOCK, UNLOCK, REPORT, PROPPATCH";
if ( $request->path == '/' ) {
$exists = true;
$allowed = "OPTIONS, GET, HEAD, PROPFIND, REPORT";
}
}
header( "Allow: $allowed");
/**
* From reading the "Scheduling Extensions to CalDAV" draft I don't think that we will
* be doing 'calendar-schedule' any time soon. The current spec is at:
* http://www.ietf.org/internet-drafts/draft-desruisseaux-caldav-sched-02.txt
*
* access-control is rfc3744, so we will say we do it, but I doubt if we do it
* in all (or even much of) it's glory really.
*/
$dav = "1, 2, access-control, calendar-access";
header( "Allow: $allowed");
header( "DAV: $dav");
// header( "DAV: 1, 2, access-control, calendar-access, calendar-schedule");
$request->DoResponse( 200, "" );

View File

@ -2,8 +2,8 @@
/**
* CalDAV Server - handle PROPFIND method
*
* @package rscds
* @subpackage caldav
* @package davical
* @subpackage propfind
* @author Andrew McMillan <andrew@catalyst.net.nz>
* @copyright Catalyst .Net Ltd
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
@ -22,78 +22,211 @@ $attribute_list = array();
$unsupported = array();
$arbitrary = array();
$namespaces = array( "DAV:" => "" );
$prefixes = array();
function add_namespace( $prefix, $namespace ) {
global $namespaces;
global $prefixes;
if ( !isset($namespaces[$namespace]) ) {
if ( $prefix == "" || isset($prefixes[$prefix]) ) {
dbg_error_log("ERROR", "Cannot assign the same prefix to two different namespaces");
exit;
}
else {
$prefixes[$prefix] = $prefix;
$namespaces[$namespace] = $prefix;
}
}
else {
if ( $namespaces[$namespace] != $prefix ) {
dbg_error_log("ERROR", "Cannot use the same namespace with two different prefixes");
exit;
}
}
}
function ns_tag( $in_tag, $namespace=null, $prefix=null ) {
global $namespaces, $prefixes;
if ( $namespace == null ) {
// Attempt to split out from namespace:tag
if ( preg_match('/^(.*):([^:]+)$/', $in_tag, $matches) ) {
$namespace = $matches[1];
$tag = $matches[2];
}
else {
// There is nothing we can do here
return $in_tag;
}
}
else {
$tag = $in_tag;
}
$namespace = strtolower($namespace);
if ( $namespace == 'dav:' ) $namespace = 'DAV:'; // Special case for conventional naming
$tag = strtolower($tag);
if ( $prefix == null ) {
// Attempt to assign one
if ( isset($namespaces[$namespace]) ) {
$prefix = $namespaces[$namespace];
}
else {
// Try and build a prefix based on the first alphabetic character of the last element of the namespace
if ( preg_match('/^(.*):([^:]+)$/', $namespace, $matches) ) {
$alpha = preg_replace( '/[^a-z]/i', '', $matches[2] );
$prefix = strtoupper(substr($alpha,0,1));
}
else {
$prefix = 'x';
}
$i = "";
if ( isset($prefixes[$prefix]) ) {
for ( $i=1; $i<10 && isset($prefixes["$prefix$i"]); $i++ ) {
}
}
if ( isset($prefixes["$prefix$i"]) ) {
dbg_error_log("ERROR", "Cannot find a free prefix for this namespace");
exit;
}
$prefix = "$prefix$i";
$namespaces[$namespace] = $prefix;
$prefixes[$prefix] = 1;
}
}
if ( !isset($namespaces[$namespace]) ) {
add_namespace( $prefix, $namespace );
}
return $prefix . ($prefix == "" ? "" : ":") . $tag;
}
function namespace_array() {
global $namespaces;
$ns = array();
foreach( $namespaces AS $n => $p ) {
if ( $p == "" ) $ns["xmlns"] = $n; else $ns["xmlns:$p"] = $n;
}
return $ns;
}
function caldav_tag( $tag ) {
return ns_tag( $tag, 'urn:ietf:params:xml:ns:caldav' );
}
foreach( $request->xml_tags AS $k => $v ) {
$tag = $v['tag'];
$ns_tag = $v['tag'];
if ( preg_match('/^(.*):([^:]+)$/', $ns_tag, $matches) ) {
$namespace = $matches[1];
$tag = $matches[2];
}
else {
$namespace = "";
$tag = $ns_tag;
}
dbg_error_log( "PROPFIND", " Handling Tag '%s' => '%s' ", $k, $v );
switch ( $tag ) {
case 'DAV::PROPFIND':
case 'DAV::PROP':
case 'PROPFIND':
case 'PROP':
dbg_error_log( "PROPFIND", ":Request: %s -> %s", $v['type'], $tag );
break;
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DESCRIPTION':
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-TIMEZONE':
case 'URN:IETF:PARAMS:XML:NS:CALDAV:SUPPORTED-CALENDAR-DATA':
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-RESOURCE-SIZE':
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MIN-DATE-TIME':
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-DATE-TIME':
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-INSTANCES':
case 'URN:IETF:PARAMS:XML:NS:CALDAV:MAX-ATTENDEES-PER-INSTANCE':
case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-HOME-SET':
case 'HTTP://APACHE.ORG/DAV/PROPS/:EXECUTABLE':
case 'DAV::CHECKED-OUT':
case 'DAV::CHECKED-IN':
case 'DAV::SOURCE':
case 'DAV::LOCKDISCOVERY':
/** These are ignored */
break;
case 'DAV::ACL': /** acl - only vaguely supported */
case 'DAV::CREATIONDATE': /** creationdate - should work fine */
case 'DAV::GETLASTMODIFIED': /** getlastmodified - should work fine */
case 'DAV::DISPLAYNAME': /** displayname - should work fine */
case 'DAV::GETCONTENTLENGTH': /** getcontentlength- should work fine */
case 'DAV::GETCONTENTTYPE': /** getcontenttype - should work fine */
case 'DAV::GETETAG': /** getetag - should work fine */
case 'DAV::SUPPORTEDLOCK': /** supportedlock - should work fine */
case 'DAV::RESOURCETYPE': /** resourcetype - should work fine */
case 'DAV::GETCONTENTLANGUAGE': /** resourcetype - should return the user's chosen locale, or default locale */
case 'DAV::SUPPORTED-PRIVILEGE-SET': /** supported-privilege-set - should work fine */
case 'URN:IETF:PARAMS:XML:NS:CALDAV:SUPPORTED-COLLATION-SET': /** fixed server definition - should work fine */
case 'URN:IETF:PARAMS:XML:NS:CALDAV:SUPPORTED-CALENDAR-COMPONENT-SET': /** fixed server definition - should work fine */
case 'DAV::CURRENT-USER-PRIVILEGE-SET': /** current-user-privilege-set - only vaguely supported */
case 'DAV::ALLPROP': /** allprop - limited support */
$attribute = substr($v['tag'],5);
$attribute_list[$attribute] = 1;
dbg_error_log( "PROPFIND", "Adding attribute '%s'", $attribute );
break;
case 'DAV::HREF':
// dbg_log_array( "PROPFIND", "DAV::HREF", $v, true );
case 'HREF':
// dbg_log_array( "PROPFIND", "HREF", $v, true );
$href_list[] = $v['value'];
dbg_error_log( "PROPFIND", "Adding attribute '%s'", $attribute );
dbg_error_log( "PROPFIND", "Adding href '%s'", $v['value'] );
break;
/**
* Add the ones that are specifically unsupported here.
* Handled DAV properties
*/
case 'UNSUPPORTED':
if ( preg_match('/^(.*):([^:]+)$/', $tag, $matches) ) {
$unsupported[$matches[2]] = $matches[1];
case 'ACL': /** acl - only vaguely supported */
case 'CREATIONDATE': /** creationdate - should work fine */
case 'GETLASTMODIFIED': /** getlastmodified - should work fine */
case 'DISPLAYNAME': /** displayname - should work fine */
case 'GETCONTENTLENGTH': /** getcontentlength- should work fine */
case 'GETCONTENTTYPE': /** getcontenttype - should work fine */
case 'GETETAG': /** getetag - should work fine */
case 'SUPPORTEDLOCK': /** supportedlock - should work fine */
case 'PRINCIPAL-URL': /** principal-url - should work fine */
case 'RESOURCETYPE': /** resourcetype - should work fine */
case 'GETCONTENTLANGUAGE': /** resourcetype - should return the user's chosen locale, or default locale */
case 'SUPPORTED-PRIVILEGE-SET': /** supported-privilege-set - should work fine */
case 'CURRENT-USER-PRIVILEGE-SET': /** current-user-privilege-set - only vaguely supported */
case 'ALLPROP': /** allprop - limited support */
/**
* Handled CalDAV properties
*/
case 'CALENDAR-HOME-SET': /** calendar-home-set is used by iCal in Leopard - should work fine */
$attribute_list[$tag] = 1;
dbg_error_log( "PROPFIND", "Adding %s attribute '%s'", $namespace, $tag );
break;
case 'SUPPORTED-COLLATION-SET': /** fixed server definition - should work fine */
case 'SUPPORTED-CALENDAR-COMPONENT-SET': /** fixed server definition - should work fine */
/**
* Handled calendar-schedule properties
*/
case 'CALENDAR-USER-ADDRESS-SET': /** CalDAV+s: slightly supported */
// case 'SCHEDULE-INBOX-URL': /** CalDAV+s: not supported */
// case 'SCHEDULE-OUTBOX-URL': /** CalDAV+s: not supported */
// case 'DROPBOX-HOME-URL': // HTTP://CALENDARSERVER.ORG/NS/
// case 'NOTIFICATIONS-URL': // HTTP://CALENDARSERVER.ORG/NS/
if ( $_SERVER['PATH_INFO'] == '/' || $_SERVER['PATH_INFO'] == '' ) {
$arbitrary[$ns_tag] = $ns_tag;
dbg_error_log( "PROPFIND", "Adding arbitrary DAV property '%s'", $ns_tag );
}
else {
$unsupported[$tag] = "";
$attribute_list[$tag] = 1;
dbg_error_log( "PROPFIND", "Adding %s attribute '%s'", $namespace, $tag );
}
dbg_error_log( "PROPFIND", "Unsupported tag >>%s<<", $tag);
break;
case 'CALENDAR-TIMEZONE': // CalDAV
case 'SUPPORTED-CALENDAR-DATA': // CalDAV
case 'MAX-RESOURCE-SIZE': // CalDAV
case 'MIN-DATE-TIME': // CalDAV
case 'MAX-DATE-TIME': // CalDAV
case 'MAX-INSTANCES': // CalDAV
case 'MAX-ATTENDEES-PER-INSTANCE': // CalDAV
// case 'CHECKED-OUT': // DAV:
// case 'CHECKED-IN': // DAV:
// case 'SOURCE': // DAV:
// case 'LOCKDISCOVERY': // DAV:
// case 'EXECUTABLE': // HTTP://APACHE.ORG/DAV/PROPS/
/** These are ignored specifically */
break;
/**
* Add the ones that are specifically unsupported here.
*/
case 'This is not a supported property': // an impossible example
$unsupported[$tag] = "";
dbg_error_log( "PROPFIND", "Unsupported tag >>%s<< in xmlns >>%s<<", $tag, $namespace);
break;
/**
* Arbitrary DAV properties may also be reported
*/
case 'CALENDAR-DESCRIPTION': // CalDAV, informational
default:
$arbitrary[$tag] = $tag;
dbg_error_log( "PROPFIND", "Adding arbitrary DAV property '%s'", $attribute );
$arbitrary[$ns_tag] = $ns_tag;
dbg_error_log( "PROPFIND", "Adding arbitrary DAV property '%s'", $ns_tag );
break;
}
}
@ -119,7 +252,7 @@ function privileges($privilege_names, $container="privilege") {
function get_arbitrary_properties($dav_name) {
global $arbitrary;
$results = array();
$results = (object) array( 'found' => array(), 'missing' => $arbitrary );
if ( count($arbitrary) > 0 ) {
$sql = "";
@ -128,7 +261,8 @@ function get_arbitrary_properties($dav_name) {
}
$qry = new PgQuery("SELECT property_name, property_value FROM property WHERE dav_name=? AND property_name IN ($sql)", $dav_name );
while( $qry->Exec("PROPFIND") && $property = $qry->Fetch() ) {
$results[$property->property_name] = $property->property_value;
$results->found[$property->property_name] = $property->property_value;
unset($results->missing[$property->property_name]);
}
}
@ -136,6 +270,45 @@ function get_arbitrary_properties($dav_name) {
}
/**
* Handles any properties related to the DAV::PRINCIPAL in the request
*/
function add_principal_properties( &$prop, &$not_found, &$denied ) {
global $attribute_list, $session, $c, $request;
if ( isset($attribute_list['PRINCIPAL-URL'] ) ) {
$prop->NewElement("principal-url", new XMLElement('href', $request->principal->url ) );
}
if ( isset($attribute_list['CALENDAR-HOME-SET'] ) ) {
$prop->NewElement(caldav_tag("calendar-home-set"), new XMLElement('href', $request->principal->calendar_home_set ) );
}
if ( isset($attribute_list['SCHEDULE-INBOX-URL'] ) ) {
$prop->NewElement(caldav_tag("schedule-inbox-url"), new XMLElement('href', $request->principal->schedule_inbox_url) );
}
if ( isset($attribute_list['SCHEDULE-OUTBOX-URL'] ) ) {
$prop->NewElement(caldav_tag("schedule-outbox-url"), new XMLElement('href', $request->principal->schedule_outbox_url) );
}
if ( isset($attribute_list['DROPBOX-HOME-URL'] ) ) {
add_namespace("A", "http://calendarserver.org/ns/");
$prop->NewElement("A:dropbox-home-url", new XMLElement('href', $request->principal->dropbox_url) );
}
if ( isset($attribute_list['NOTIFICATIONS-URL'] ) ) {
add_namespace("A", "http://calendarserver.org/ns/");
$prop->NewElement("A:notifications-url", new XMLElement('href', $request->principal->notifications_url) );
}
if ( isset($attribute_list['CALENDAR-USER-ADDRESS-SET'] ) ) {
$addr_set = array();
foreach( $request->principal->user_address_set AS $k => $v ) {
$addr_set[] = new XMLElement('href', $v );
}
$prop->NewElement(caldav_tag("calendar-user-address-set"), $addr_set );
}
}
/**
* Returns an XML sub-tree for a single collection record from the DB
*/
@ -144,13 +317,15 @@ function collection_to_xml( $collection ) {
dbg_error_log("PROPFIND","Building XML Response for collection '%s'", $collection->dav_name );
$collection->properties = get_arbitrary_properties($collection->dav_name);
$arbitrary_results = get_arbitrary_properties($collection->dav_name);
$collection->properties = $arbitrary_results->found;
$url = ConstructURL($collection->dav_name);
$url = $_SERVER['SCRIPT_NAME'] . $collection->dav_name;
$resourcetypes = array( new XMLElement("collection") );
$contentlength = false;
if ( $collection->is_calendar == 't' ) {
$resourcetypes[] = new XMLElement("calendar", false, array("xmlns" => "urn:ietf:params:xml:ns:caldav"));
$resourcetypes[] = new XMLElement(caldav_tag("calendar"), false);
$lqry = new PgQuery("SELECT sum(length(caldav_data)) FROM caldav_data WHERE user_no = ? AND dav_name ~ ?;", $collection->user_no, $collection->dav_name.'[^/]+$' );
if ( $lqry->Exec("PROPFIND",__LINE__,__FILE__) && $row = $lqry->Fetch() ) {
$contentlength = $row->sum;
@ -160,21 +335,23 @@ function collection_to_xml( $collection ) {
$resourcetypes[] = new XMLElement("principal");
}
$prop = new XMLElement("prop");
$not_found = new XMLElement("prop");
$denied = new XMLElement("prop");
/**
* First process any static values we do support
*/
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['SUPPORTED-COLLATION-SET']) ) {
$collations = array();
$collations[] = new XMLElement("supported-collation", 'i;ascii-casemap');
$collations[] = new XMLElement("supported-collation", 'i;octet');
$prop->NewElement("supported-collation-set", $collations, array("xmlns" => "urn:ietf:params:xml:ns:caldav") );
$collations[] = new XMLElement(caldav_tag("supported-collation"), 'i;ascii-casemap');
$collations[] = new XMLElement(caldav_tag("supported-collation"), 'i;octet');
$prop->NewElement(caldav_tag("supported-collation-set"), $collations );
}
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['SUPPORTED-CALENDAR-COMPONENT-SET']) ) {
$components = array();
$components[] = new XMLElement("comp", '', array("name" => "VEVENT"));
$components[] = new XMLElement("comp", '', array("name" => "VTODO"));
$prop->NewElement("supported-calendar-component-set", $components, array("xmlns" => "urn:ietf:params:xml:ns:caldav") );
$components[] = new XMLElement(caldav_tag("comp"), '', array("name" => "VEVENT"));
$components[] = new XMLElement(caldav_tag("comp"), '', array("name" => "VTODO"));
$prop->NewElement(caldav_tag("supported-calendar-component-set"), $components );
}
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETCONTENTTYPE']) ) {
$prop->NewElement("getcontenttype", "httpd/unix-directory" );
@ -206,9 +383,31 @@ function collection_to_xml( $collection ) {
$prop->NewElement("current-user-privilege-set", privileges($request->permissions) );
}
if ( count($arbitrary) > 0 ) {
foreach( $arbitrary AS $k => $v ) {
$prop->NewElement($k, $collection->properties[$k]);
if ( isset($attribute_list['CALENDAR-FREE-BUSY-SET'] ) ) {
if ( isset($collection->is_inbox) && $collection->is_inbox && $session->user_no == $collection->user_no ) {
$fb_set = array();
foreach( $collection->free_busy_set AS $k => $v ) {
$fb_set[] = new XMLElement('href', $v );
}
$prop->NewElement(caldav_tag("calendar-free-busy-set"), $fb_set );
}
else if ( $session->user_no == $collection->user_no ) {
$not_found->NewElement(caldav_tag("calendar-free-busy-set") );
}
else {
$denied->NewElement(caldav_tag("calendar-free-busy-set") );
}
}
/**
* Then look at any properties related to the principal
*/
add_principal_properties( $prop, $not_found, $denied );
if ( count($collection->properties) > 0 ) {
foreach( $collection->properties AS $k => $v ) {
$prop->NewElement(ns_tag($k), $v);
}
}
@ -245,8 +444,23 @@ function collection_to_xml( $collection ) {
$propstat = new XMLElement( "propstat", array( $prop, $status) );
$href = new XMLElement("href", $url );
$response = array($href,$propstat);
$response = new XMLElement( "response", array($href,$propstat));
if ( count($arbitrary_results->missing) > 0 ) {
foreach( $arbitrary_results->missing AS $k => $v ) {
$not_found->NewElement(ns_tag($k), '');
}
}
if ( is_array($not_found->content) && count($not_found->content) > 0 ) {
$response[] = new XMLElement( "propstat", array( $not_found, new XMLElement("status", "HTTP/1.1 404 Not Found" )) );
}
if ( is_array($denied->content) && count($denied->content) > 0 ) {
$response[] = new XMLElement( "propstat", array( $denied, new XMLElement("status", "HTTP/1.1 403 Forbidden" )) );
}
$response = new XMLElement( "response", $response );
return $response;
}
@ -262,8 +476,13 @@ function item_to_xml( $item ) {
$item->properties = get_arbitrary_properties($item->dav_name);
$url = $_SERVER['SCRIPT_NAME'] . $item->dav_name;
$url = ConstructURL($item->dav_name);
$prop = new XMLElement("prop");
$not_found = new XMLElement("prop");
$denied = new XMLElement("prop");
if ( isset($attribute_list['ALLPROP']) || isset($attribute_list['GETLASTMODIFIED']) ) {
$prop->NewElement("getlastmodified", ( isset($item->modified)? $item->modified : false ));
}
@ -290,6 +509,11 @@ function item_to_xml( $item ) {
$prop->NewElement("getetag", '"'.$item->dav_etag.'"' );
}
/**
* Then look at any properties related to the principal
*/
add_principal_properties( $prop, $not_found, $denied );
if ( isset($attribute_list['ACL']) ) {
/**
* FIXME: This information is semantically valid but presents an incorrect picture.
@ -322,8 +546,17 @@ function item_to_xml( $item ) {
$propstat = new XMLElement( "propstat", array( $prop, $status) );
$href = new XMLElement("href", $url );
$response = array($href,$propstat);
$response = new XMLElement( "response", array($href,$propstat));
if ( is_array($not_found->content) && count($not_found->content) > 0 ) {
$response[] = new XMLElement( "propstat", array( $not_found, new XMLElement("status", "HTTP/1.1 404 Not Found" )) );
}
if ( is_array($denied->content) && count($denied->content) > 0 ) {
$response[] = new XMLElement( "propstat", array( $denied, new XMLElement("status", "HTTP/1.1 403 Forbidden" )) );
}
$response = new XMLElement( "response", $response );
return $response;
}
@ -363,7 +596,7 @@ function get_collection_contents( $depth, $user_no, $collection ) {
while( $subcollection = $qry->Fetch() ) {
$responses[] = collection_to_xml( $subcollection );
if ( $depth > 0 ) {
$responses = array_merge( $responses, get_collection( $depth - 1, $user_no, $subcollection->dav_name ) );
$responses = array_merge( $responses, get_collection_contents( $depth - 1, $user_no, $subcollection ) );
}
}
}
@ -380,7 +613,7 @@ function get_collection_contents( $depth, $user_no, $collection ) {
$sql .= "to_char(last_modified at time zone 'GMT',?) AS modified, ";
$sql .= "summary AS dav_displayname ";
$sql .= "FROM caldav_data JOIN calendar_item USING( user_no, dav_name) WHERE dav_name ~ ".qpg('^'.$collection->dav_name.'[^/]+$');
$sql .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL OR get_permissions($session->user_no,caldav_data.user_no) ~ 'A') "; // Must have 'all' permissions to see confidential items
$sql .= " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL OR get_permissions($session->user_no,caldav_data.user_no) ~ 'A') "; // Must have 'all' permissions to see confidential items
$sql .= "ORDER BY caldav_data.dav_name ";
$qry = new PgQuery($sql, PgQuery::Plain(iCalendar::HttpDateFormat()), PgQuery::Plain(iCalendar::HttpDateFormat()));
if( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows > 0 ) {
@ -404,12 +637,13 @@ function get_collection( $depth, $user_no, $collection_path ) {
dbg_error_log("PROPFIND","Getting collection: Depth %d, User: %d, Path: %s", $depth, $user_no, $collection_path );
if ( $collection_path == '/' ) {
if ( $collection_path == null || $collection_path == '/' || $collection_path == '' ) {
$collection->dav_name = $collection_path;
$collection->dav_etag = md5($c->system_name . $collection_path);
$collection->is_calendar = 'f';
$collection->is_principal = 'f';
$collection->dav_displayname = $c->system_name;
$collection->created = date('Ymd"T"His');
$collection->created = date('Ymd\THis');
$responses[] = collection_to_xml( $collection );
}
else {
@ -434,6 +668,7 @@ function get_collection( $depth, $user_no, $collection_path ) {
$collection->dav_name = $collection_path;
$collection->dav_etag = md5($collection_path);
$collection->is_calendar = 't'; // Everything is a calendar, if it always exists!
$collection->is_principal = 'f';
$collection->dav_displayname = $collection_path;
$collection->created = date('Ymd"T"His');
$responses[] = collection_to_xml( $collection );
@ -445,6 +680,7 @@ function get_collection( $depth, $user_no, $collection_path ) {
return $responses;
}
/**
* Get XML response for a single item. Depth is irrelevant for this.
*/
@ -458,7 +694,7 @@ function get_item( $item_path ) {
$sql .= "to_char(coalesce(calendar_item.created, caldav_data.created) at time zone 'GMT',?) AS created, ";
$sql .= "to_char(last_modified at time zone 'GMT',?) AS modified, ";
$sql .= "summary AS dav_displayname ";
$sql .= "FROM caldav_data JOIN calendar_item USING( user_no, dav_name) WHERE dav_name = ?";
$sql .= "FROM caldav_data JOIN calendar_item USING( user_no, dav_name) WHERE dav_name = ? ";
$sql .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL OR get_permissions($session->user_no,caldav_data.user_no) ~ 'A') "; // Must have 'all' permissions to see confidential items
$qry = new PgQuery($sql, PgQuery::Plain(iCalendar::HttpDateFormat()), PgQuery::Plain(iCalendar::HttpDateFormat()), $item_path);
if( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows > 0 ) {
@ -487,7 +723,7 @@ else {
$request->DoResponse( 403, translate("You do not have appropriate rights to view that resource.") );
}
$multistatus = new XMLElement( "multistatus", $responses, array('xmlns'=>'DAV:') );
$multistatus = new XMLElement( "multistatus", $responses, namespace_array() );
// dbg_log_array( "PROPFIND", "XML", $multistatus, true );
$xmldoc = $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
@ -495,4 +731,3 @@ $etag = md5($xmldoc);
header("ETag: \"$etag\"");
$request->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
?>

View File

@ -195,7 +195,8 @@ if ( count($failure) > 0 ) {
));
}
array_unshift( $failure, new XMLElement('href', $c->protocol_server_port_script . $request->path ) );
$url = ConstructURL($request->path);
array_unshift( $failure, new XMLElement('href', $url ) );
$failure[] = new XMLElement('responsedescription', translate("Some properties were not able to be changed.") );
$multistatus = new XMLElement( "multistatus", new XMLElement( 'response', $failure ), array('xmlns'=>'DAV:') );
@ -209,7 +210,8 @@ if ( count($failure) > 0 ) {
$sql .= "COMMIT;";
$qry = new PgQuery( $sql );
if ( $qry->Exec() ) {
$href = new XMLElement('href', $c->protocol_server_port_script . $request->path );
$url = ConstructURL($request->path);
$href = new XMLElement('href', $url );
$desc = new XMLElement('responsedescription', translate("All requested changes were made.") );
$multistatus = new XMLElement( "multistatus", new XMLElement( 'response', array( $href, $desc ) ), array('xmlns'=>'DAV:') );

View File

@ -8,7 +8,38 @@
include_once("iCalendar.php");
function controlRequestContainer( $username, $user_no, $path, $context ) {
/**
* This function launches an error
* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
* @param int $user_no the user wich will receive this ics file
* @param string $path the $path where the PUT failed to store such as /user_foo/home/
* @param string $message An optional error message to return to the client
* @param int $error_no An optional value for the HTTP error code
*/
function rollback_on_error( $caldav_context, $user_no, $path, $message='', $error_no=500 ) {
if ( !$message ) $message = translate("Database error");
$qry = new PgQuery("ROLLBACK;"); $qry->Exec("PUT-collection");
if ( $caldav_context ) {
global $request;
$request->DoResponse( $error_no, $message );
}
else {
global $c;
$c->messages[] = sprintf("Status: %d, Message: %s, User: %d, Path: %s", $error_no, $message, $user_no, $path);
}
}
/**
* Work out the location we are doing the PUT to, and check that we have the rights to
* do the needful.
* @param string $username The name of the destination user
* @param int $user_no The user making the change
* @param string $path The DAV path the resource is bing PUT to
* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
*/
function controlRequestContainer( $username, $user_no, $path, $caldav_context ) {
// Check to see if the path is like /foo /foo/bar or /foo/bar/baz etc. (not ending with a '/', but contains at least one)
if ( preg_match( '#^(.*/)([^/]+)$#', $path, $matches ) ) {//(
@ -34,14 +65,7 @@ function controlRequestContainer( $username, $user_no, $path, $context ) {
$sql = "SELECT * FROM collection WHERE user_no = ? AND dav_name = ?;";
$qry = new PgQuery( $sql, $user_no, $request_container );
if ( ! $qry->Exec("PUT") ) {
if($context){
global $request;
$request->DoResponse( 500, translate("Error querying database.") );
}
else {
global $c;
$c->messages[] = sprintf("Status: %d, Message: %s, User: %d, Path: %s", 500, translate("Error querying database."),$user_no,$path);
}
rollback_on_error( $caldav_context, $user_no, $path );
}
if ( $qry->rows == 0 ) {
if ( preg_match( '#^(.*/)([^/]+/)$#', $request_container, $matches ) ) {//(
@ -59,21 +83,32 @@ function controlRequestContainer( $username, $user_no, $path, $context ) {
}
/**
* This function launches an error
* @param int $user_no the user wich will receive this ics file
* @param string $path the $path where it will be store such as /user_foo/home/
* @param boolean $context is true if this function is called from a way where $request is defined
* Check if this collection should force all events to be PUBLIC.
* @param string $user_no the user that owns the collection
* @param string $dav_name the collection to check
* @return boolean Return true if public events only are allowed.
*/
function rollback_on_error($context,$user_no,$path) {
$qry = new PgQuery("ROLLBACK;"); $qry->Exec("PUT-collection");
if($context){
global $request;
$request->DoResponse( 500, translate("Database error") );
}
else {
global $c;
$c->messages[] = sprintf("Status: %d, Message: %s, User: %d, Path: %s", 500, translate("Database error"),$user_no,$path);
function public_events_only( $user_no, $dav_name ) {
global $c;
// Not supported until DB versions from 1.001.010
if ( $c->schema_version < 1001.010 ) return false;
$sql = "SELECT public_events_only ";
$sql .= "FROM collection ";
$sql .= "WHERE user_no=? AND dav_name=?";
$qry = new PgQuery($sql, $user_no, $dav_name);
if( $qry->Exec('PUT') && $qry->rows == 1 ) {
$collection = $qry->Fetch();
if ($collection->public_events_only == 't') {
return true;
}
}
// Something went wrong, must be false.
return false;
}
@ -82,15 +117,18 @@ function rollback_on_error($context,$user_no,$path) {
* @param string $ics_content the ics file to import
* @param int $user_no the user wich will receive this ics file
* @param string $path the $path where it will be store such as /user_foo/home/
* @param boolean $context is true if this function is called from a way where $request is defined
* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
*/
function import_collection($ics_content, $user_no, $path,$context){
function import_collection( $ics_content, $user_no, $path, $caldav_context ) {
global $c;
// According to RFC2445 we should always end with CRLF, but the CalDAV spec says
// that normalising XML parses often muck with it and may remove the CR.
$icalendar = preg_replace('/\r?\n /', '', $ics_content );
$fh = fopen('/tmp/PUT-2.txt','w');
fwrite($fh,$icalendar);
fclose($fh);
if ( isset($c->dbg['ALL']) || isset($c->dbg['put']) ) {
$fh = fopen('/tmp/PUT-2.txt','w');
fwrite($fh,$icalendar);
fclose($fh);
}
$lines = preg_split('/\r?\n/', $icalendar );
@ -132,7 +170,7 @@ function import_collection($ics_content, $user_no, $path,$context){
}
}
$qry = new PgQuery("BEGIN; DELETE FROM calendar_item WHERE user_no=? AND dav_name ~ ?; DELETE FROM caldav_data WHERE user_no=? AND dav_name ~ ?;", $user_no, $path.'[^/]+$', $user_no, $path.'[^/]+$');
if ( !$qry->Exec("PUT") ) rollback_on_error($context,$user_no,$path);
if ( !$qry->Exec("PUT") ) rollback_on_error( $caldav_context, $user_no, $path );
foreach( $events AS $k => $event ) {
dbg_error_log( "PUT", "Putting event %d with data: %s", $k, $event['data'] );
@ -142,7 +180,7 @@ function import_collection($ics_content, $user_no, $path,$context){
$event_path = sprintf( "%s%d.ics", $path, $k);
$qry = new PgQuery( "INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified ) VALUES( ?, ?, ?, ?, ?, ?, current_timestamp, current_timestamp )",
$user_no, $event_path, $etag, $icalendar, $ic->type, $session->user_no );
if ( !$qry->Exec("PUT") ) rollback_on_error($context,$user_no,$path);
if ( !$qry->Exec("PUT") ) rollback_on_error( $caldav_context, $user_no, $path );
$sql = "";
if ( preg_match(':^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Brazil|Canada|Chile|Etc|Europe|Indian|Mexico|Mideast|Pacific|US)/[a-z]+$:i', $ic->tz_locn ) ) {
@ -175,6 +213,21 @@ function import_collection($ics_content, $user_no, $path,$context){
$dtstamp = $last_modified;
}
$class = $ic->Get("class");
/* Check and see if we should over ride the class. */
if ( public_events_only($user_no, $path) ) {
$class = 'PUBLIC';
}
/*
* It seems that some calendar clients don't set a class...
* RFC2445, 4.8.1.3:
* Default is PUBLIC
*/
if ( !isset($class) || $class == '' ) {
$class = 'PUBLIC';
}
$sql .= <<<EOSQL
INSERT INTO calendar_item (user_no, dav_name, dav_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp,
description, rrule, tz_id, last_modified, url, priority, created, due, percent_complete )
@ -183,14 +236,165 @@ EOSQL;
$qry = new PgQuery( $sql, $user_no, $event_path, $etag, $ic->Get('uid'), $dtstamp,
$ic->Get('dtstart'), $ic->Get('summary'), $ic->Get('location'),
$ic->Get('class'), $ic->Get('transp'), $ic->Get('description'), $ic->Get('rrule'), $ic->Get('tz_id'),
$class, $ic->Get('transp'), $ic->Get('description'), $ic->Get('rrule'), $ic->Get('tz_id'),
$last_modified, $ic->Get('url'), $ic->Get('priority'), $ic->Get('created'),
$ic->Get('due'), $ic->Get('percent-complete')
);
if ( !$qry->Exec("PUT") ) rollback_on_error($context,$user_no,$path);
if ( !$qry->Exec("PUT") ) rollback_on_error( $caldav_context, $user_no, $path);
}
$qry = new PgQuery("COMMIT;");
if ( !$qry->Exec("PUT") ) rollback_on_error($context,$user_no,$path);
if ( !$qry->Exec("PUT") ) rollback_on_error( $caldav_context, $user_no, $path);
}
?>
/**
* Put the resource from this request
* @param object $request A reference to the request object
* @param int $author The user_no who wants to put this resource on the server
* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
* @return string Either 'INSERT' or 'UPDATE': the type of action that the PUT resulted in
*/
function putCalendarResource( &$request, $author, $caldav_context ) {
$etag = md5($request->raw_post);
$ic = new iCalendar(array( 'icalendar' => $request->raw_post ));
dbg_log_array( "PUT", 'EVENT', $ic->properties['VCALENDAR'][0], true );
/**
* We read any existing object so we can check the ETag.
*/
unset($put_action_type);
$qry = new PgQuery( "SELECT * FROM caldav_data WHERE user_no=? AND dav_name=?", $request->user_no, $request->path );
if ( !$qry->Exec("PUT") || $qry->rows > 1 ) {
rollback_on_error( $caldav_context, $request->user_no, $request->path );
}
elseif ( $qry->rows < 1 ) {
if ( isset($request->etag_if_match) && $request->etag_if_match != '' ) {
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*/
rollback_on_error( $caldav_context, $request->user_no, $request->path, 412, translate("Resource changed on server - not changed.") );
}
$put_action_type = 'INSERT';
if ( ! $request->AllowedTo("create") ) {
rollback_on_error( $caldav_context, $request->user_no, $request->path, 403, translate("You may not add entries to this calendar.") );
}
}
elseif ( $qry->rows == 1 ) {
$icalendar = $qry->Fetch();
if ( ( isset($request->etag_if_match) && $request->etag_if_match != '' && $request->etag_if_match != $icalendar->dav_etag )
|| ( isset($request->etag_none_match) && $request->etag_none_match != '' && ($request->etag_none_match == $icalendar->dav_etag || $request->etag_none_match == '*') ) ) {
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*
* RFC2068, 14.26:
* If any of the entity tags match the entity tag of the entity that
* would have been returned in the response to a similar GET request
* (without the If-None-Match header) on that resource, or if "*" is
* given and any current entity exists for that resource, then the
* server MUST NOT perform the requested method.
*/
if ( isset($request->etag_if_match) && $request->etag_if_match != $icalendar->dav_etag ) {
$error = translate( "Existing resource does not match 'If-Match' header - not accepted.");
}
if ( isset($etag_none_match) && $etag_none_match != '' && ($etag_none_match == $icalendar->dav_etag || $etag_none_match == '*') ) {
$error = translate( "Existing resource matches 'If-None-Match' header - not accepted.");
}
$request->DoResponse( 412, $error );
}
$put_action_type = 'UPDATE';
if ( ! $request->AllowedTo("modify") ) {
$request->DoResponse( 403, translate("You may not modify entries on this calendar.") );
}
}
if ( $put_action_type == 'INSERT' ) {
$qry = new PgQuery( "BEGIN; INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified ) VALUES( ?, ?, ?, ?, ?, ?, current_timestamp, current_timestamp )",
$request->user_no, $request->path, $etag, $request->raw_post, $ic->type, $author );
if ( !$qry->Exec("PUT") ) rollback_on_error( $caldav_context, $request->user_no, $request->path);
}
else {
$qry = new PgQuery( "BEGIN;UPDATE caldav_data SET caldav_data=?, dav_etag=?, caldav_type=?, logged_user=?, modified=current_timestamp WHERE user_no=? AND dav_name=?",
$request->raw_post, $etag, $ic->type, $author, $request->user_no, $request->path );
if ( !$qry->Exec("PUT") ) rollback_on_error( $caldav_context, $request->user_no, $request->path);
}
$sql = ( $ic->tz_locn == '' ? '' : "SET TIMEZONE TO ".qpg($ic->tz_locn).";" );
$dtstart = $ic->Get('DTSTART');
if ( (!isset($dtstart) || $dtstart == "") && $ic->Get('DUE') != "" ) {
$dtstart = $ic->Get('DUE');
}
$dtend = $ic->Get('DTEND');
if ( (!isset($dtend) || "$dtend" == "") && $ic->Get('DURATION') != "" AND $dtstart != "" ) {
$duration = preg_replace( '#[PT]#', ' ', $ic->Get('DURATION') );
$dtend = '('.qpg($dtstart).'::timestamp with time zone + '.qpg($duration).'::interval)';
}
else {
dbg_error_log( "PUT", " DTEND: '%s', DTSTART: '%s', DURATION: '%s'", $dtend, $dtstart, $ic->Get('DURATION') );
$dtend = qpg($dtend);
}
$last_modified = $ic->Get("LAST-MODIFIED");
if ( !isset($last_modified) || $last_modified == '' ) {
$last_modified = gmdate( 'Ymd\THis\Z' );
}
$dtstamp = $ic->Get("DTSTAMP");
if ( !isset($dtstamp) || $dtstamp == '' ) {
$dtstamp = $last_modified;
}
$class = $ic->Get("class");
/* Check and see if we should over ride the class. */
if ( public_events_only($user_no, $path) ) {
$class = 'PUBLIC';
}
/*
* It seems that some calendar clients don't set a class...
* RFC2445, 4.8.1.3:
* Default is PUBLIC
*/
if ( !isset($class) || $class == '' ) {
$class = 'PUBLIC';
}
if ( $put_action_type != 'INSERT' ) {
$sql .= "DELETE FROM calendar_item WHERE user_no=$request->user_no AND dav_name=".qpg($request->path).";";
}
$sql .= <<<EOSQL
INSERT INTO calendar_item (user_no, dav_name, dav_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp,
description, rrule, tz_id, last_modified, url, priority, created, due, percent_complete, status )
VALUES ( ?, ?, ?, ?, ?, ?, $dtend, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
COMMIT;
EOSQL;
$qry = new PgQuery( $sql, $request->user_no, $request->path, $etag, $ic->Get('UID'), $dtstamp,
$ic->Get('DTSTART'), $ic->Get('SUMMARY'), $ic->Get('LOCATION'),
$class, $ic->Get('TRANSP'), $ic->Get('DESCRIPTION'), $ic->Get('RRULE'), $ic->Get('TZ_ID'),
$last_modified, $ic->Get('URL'), $ic->Get('PRIORITY'), $ic->Get('CREATED'),
$ic->Get('DUE'), $ic->Get('PERCENT-COMPLETE'), $ic->Get('STATUS')
);
if ( !$qry->Exec("PUT") ) rollback_on_error( $caldav_context, $request->user_no, $request->path);
dbg_error_log( "PUT", "User: %d, ETag: %s, Path: %s", $author, $etag, $request->path);
header(sprintf('ETag: "%s"', (isset($bogus_etag) ? $bogus_etag : $etag) ) );
return $put_action_type;
}

View File

@ -36,128 +36,5 @@ if ( $is_collection ) {
return;
}
$etag = md5($request->raw_post);
$ic = new iCalendar(array( 'icalendar' => $request->raw_post ));
dbg_log_array( "PUT", 'EVENT', $ic->properties['VCALENDAR'][0], true );
/**
* We read any existing object so we can check the ETag.
*/
unset($put_action_type);
$qry = new PgQuery( "SELECT * FROM caldav_data WHERE user_no=? AND dav_name=?", $request->user_no, $request->path );
if ( !$qry->Exec("PUT") || $qry->rows > 1 ) {
$request->DoResponse( 500, translate("Error querying database.") );
}
elseif ( $qry->rows < 1 ) {
if ( isset($request->etag_if_match) && $request->etag_if_match != '' ) {
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*/
$request->DoResponse( 412, translate("Resource changed on server - not changed.") );
}
$put_action_type = 'INSERT';
if ( ! $request->AllowedTo("create") ) {
$request->DoResponse( 403, translate("You may not add entries to this calendar.") );
}
}
elseif ( $qry->rows == 1 ) {
$icalendar = $qry->Fetch();
if ( ( isset($request->etag_if_match) && $request->etag_if_match != '' && $request->etag_if_match != $icalendar->dav_etag )
|| ( isset($request->etag_none_match) && $request->etag_none_match != '' && ($request->etag_none_match == $icalendar->dav_etag || $request->etag_none_match == '*') ) ) {
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*
* RFC2068, 14.26:
* If any of the entity tags match the entity tag of the entity that
* would have been returned in the response to a similar GET request
* (without the If-None-Match header) on that resource, or if "*" is
* given and any current entity exists for that resource, then the
* server MUST NOT perform the requested method.
*/
if ( isset($request->etag_if_match) && $request->etag_if_match != $icalendar->dav_etag ) {
$error = translate( "Existing resource does not match 'If-Match' header - not accepted.");
}
if ( isset($etag_none_match) && $etag_none_match != '' && ($etag_none_match == $icalendar->dav_etag || $etag_none_match == '*') ) {
$error = translate( "Existing resource matches 'If-None-Match' header - not accepted.");
}
$request->DoResponse( 412, $error );
}
$put_action_type = 'UPDATE';
if ( ! $request->AllowedTo("modify") ) {
$request->DoResponse( 403, translate("You may not modify entries on this calendar.") );
}
}
if ( $put_action_type == 'INSERT' ) {
$qry = new PgQuery( "INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified ) VALUES( ?, ?, ?, ?, ?, ?, current_timestamp, current_timestamp )",
$request->user_no, $request->path, $etag, $request->raw_post, $ic->type, $session->user_no );
$qry->Exec("PUT");
}
else {
$qry = new PgQuery( "UPDATE caldav_data SET caldav_data=?, dav_etag=?, caldav_type=?, logged_user=?, modified=current_timestamp WHERE user_no=? AND dav_name=?",
$request->raw_post, $etag, $ic->type, $session->user_no, $request->user_no, $request->path );
$qry->Exec("PUT");
}
$sql = "BEGIN;".( $ic->tz_locn == '' ? '' : "SET TIMEZONE TO ".qpg($ic->tz_locn).";" );
$dtstart = $ic->Get('DTSTART');
if ( (!isset($dtstart) || $dtstart == "") && $ic->Get('DUE') != "" ) {
$dtstart = $ic->Get('DUE');
}
$dtend = $ic->Get('DTEND');
if ( (!isset($dtend) || "$dtend" == "") && $ic->Get('DURATION') != "" AND $dtstart != "" ) {
$duration = preg_replace( '#[PT]#', ' ', $ic->Get('DURATION') );
$dtend = '('.qpg($dtstart).'::timestamp with time zone + '.qpg($duration).'::interval)';
}
else {
dbg_error_log( "PUT", " DTEND: '%s', DTSTART: '%s', DURATION: '%s'", $dtend, $dtstart, $ic->Get('DURATION') );
$dtend = qpg($dtend);
}
$last_modified = $ic->Get("LAST-MODIFIED");
if ( !isset($last_modified) || $last_modified == '' ) {
$last_modified = gmdate( 'Ymd\THis\Z' );
}
$dtstamp = $ic->Get("DTSTAMP");
if ( !isset($dtstamp) || $dtstamp == '' ) {
$dtstamp = $last_modified;
}
if ( $put_action_type != 'INSERT' ) {
$sql .= "DELETE FROM calendar_item WHERE user_no=$request->user_no AND dav_name=".qpg($request->path).";";
}
$sql .= <<<EOSQL
INSERT INTO calendar_item (user_no, dav_name, dav_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp,
description, rrule, tz_id, last_modified, url, priority, created, due, percent_complete, status )
VALUES ( ?, ?, ?, ?, ?, ?, $dtend, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
COMMIT;
EOSQL;
$qry = new PgQuery( $sql, $request->user_no, $request->path, $etag, $ic->Get('UID'), $dtstamp,
$ic->Get('DTSTART'), $ic->Get('SUMMARY'), $ic->Get('LOCATION'),
$ic->Get('CLASS'), $ic->Get('TRANSP'), $ic->Get('DESCRIPTION'), $ic->Get('RRULE'), $ic->Get('TZ_ID'),
$last_modified, $ic->Get('URL'), $ic->Get('PRIORITY'), $ic->Get('CREATED'),
$ic->Get('DUE'), $ic->Get('PERCENT-COMPLETE'), $ic->Get('STATUS')
);
$qry->Exec("PUT");
dbg_error_log( "PUT", "User: %d, ETag: %s, Path: %s", $session->user_no, $etag, $request->path);
header(sprintf('ETag: "%s"', (isset($bogus_etag) ? $bogus_etag : $etag) ) );
$put_action_type = putCalendarResource( $request, $session->user_no, true );
$request->DoResponse( ($put_action_type == 'INSERT' ? 201 : 204) );
?>

View File

@ -6,8 +6,8 @@ include_once("iCalendar.php");
include_once("RRule.php");
$fbq_content = $xmltree->GetContent('URN:IETF:PARAMS:XML:NS:CALDAV:FREE-BUSY-QUERY');
$fbq_start = $fbq_content[0]->GetAttribute('START');
$fbq_end = $fbq_content[0]->GetAttribute('END');
$fbq_start = $fbq_content[0]->GetAttribute('START');
$fbq_end = $fbq_content[0]->GetAttribute('END');
if ( ! ( isset($fbq_start) || isset($fbq_end) ) ) {
$request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
@ -64,11 +64,13 @@ foreach( $busy_tentative AS $k => $v ) {
while ( $date = $rrule->GetNext() ) {
if ( ! $date->GreaterThan($fbq_start) ) continue;
if ( $date->GreaterThan($fbq_end) ) break;
$freebusy .= sprintf("FREEBUSY;FBTYPE=BUSY-TENTATIVE:%s/%s\n", $date->Render('Ymd\THis'), $duration );
$todate = clone($date);
$todate->AddDuration($duration);
$freebusy .= sprintf("FREEBUSY;FBTYPE=BUSY-TENTATIVE:%s/%s\n", $date->Render('Ymd\THis'), $todate->Render('Ymd\THis') );
}
}
else {
$freebusy .= sprintf("FREEBUSY;FBTYPE=BUSY-TENTATIVE:%s/%s\n", $start->Render('Ymd\THis'), $duration );
$freebusy .= sprintf("FREEBUSY;FBTYPE=BUSY-TENTATIVE:%s/%s\n", $start->Render('Ymd\THis'), $v->finish );
}
}
@ -80,11 +82,13 @@ foreach( $busy AS $k => $v ) {
while ( $date = $rrule->GetNext() ) {
if ( ! $date->GreaterThan($fbq_start) ) continue;
if ( $date->GreaterThan($fbq_end) ) break;
$freebusy .= sprintf("FREEBUSY:%s/%s\n", $date->Render('Ymd\THis'), $duration );
$todate = clone($date);
$todate->AddDuration($duration);
$freebusy .= sprintf("FREEBUSY:%s/%s\n", $date->Render('Ymd\THis'), $todate->Render('Ymd\THis') );
}
}
else {
$freebusy .= sprintf("FREEBUSY:%s/%s\n", $start->Render('Ymd\THis'), $duration );
$freebusy .= sprintf("FREEBUSY:%s/%s\n", $start->Render('Ymd\THis'), $v->finish );
}
}

View File

@ -4,6 +4,7 @@ $responses = array();
/**
* Return XML for a single Principal (user) from the DB
* TODO: Refactor this functionality into the CalDAVPrincipal object
*
* @param array $properties The requested properties for this principal
* @param string $item The user data for this calendar
@ -15,16 +16,13 @@ function principal_to_xml( $properties, $item ) {
dbg_error_log("REPORT","Building XML Response for principal '%s'", $item->username );
$this_url = $c->protocol_server_port_script . $request->dav_name;
$principal_url = sprintf( "%s/%s/", $c->protocol_server_port_script, $item->username);
$home_calendar = sprintf( "%s/%s/%s/", $c->protocol_server_port_script, $item->username, $c->home_calendar_name);
$this_url = ConstructURL( $request->dav_name );
$principal_url = ConstructURL( "/".$item->username."/");
$home_calendar = ConstructURL( "/".$item->username."/");
$prop = new XMLElement("prop");
$denied = array();
foreach( $properties AS $k => $v ) {
switch( $v ) {
// case 'DAV::GETCONTENTTYPE':
// $prop->NewElement("getcontenttype", "text/x-vcard" );
// break;
case 'DAV::RESOURCETYPE':
$prop->NewElement("resourcetype", new XMLElement("principal") );
break;
@ -42,7 +40,7 @@ function principal_to_xml( $properties, $item ) {
$group = array();
if ( $qry->Exec("REPORT-principal") && $qry->rows > 0 ) {
while( $membership = $qry->Fetch() ) {
$group[] = new XMLElement("href", sprintf( "%s/%s/", $c->protocol_server_port_script, $membership->username) );
$group[] = new XMLElement("href", ConstructURL( "/". $membership->username . "/") );
}
}
$prop->NewElement("group-member-set", $group );
@ -52,7 +50,7 @@ function principal_to_xml( $properties, $item ) {
$group = array();
if ( $qry->Exec("REPORT-principal") && $qry->rows > 0 ) {
while( $membership = $qry->Fetch() ) {
$group[] = new XMLElement("href", sprintf( "%s/%s/", $c->protocol_server_port_script, $membership->username) );
$group[] = new XMLElement("href", ConstructURL( "/". $membership->username . "/") );
}
}
$prop->NewElement("group-membership", $group );
@ -71,7 +69,7 @@ function principal_to_xml( $properties, $item ) {
$status = new XMLElement("status", "HTTP/1.1 200 OK" );
$propstat = new XMLElement( "propstat", array( $prop, $status) );
$href = new XMLElement("href", $url );
$href = new XMLElement("href", $principal_url );
$elements = array($href,$propstat);

View File

@ -29,6 +29,8 @@ require_once("iCalendar.php");
$reportnum = -1;
$report = array();
$denied = array();
$unsupported = array();
if ( isset($prop_filter) ) unset($prop_filter);
$position = 0;
@ -69,26 +71,38 @@ function calendar_to_xml( $properties, $item ) {
if ( !is_numeric(strpos($item->permissions,'A')) && $session->user_no != $item->user_no ){
// the user is not admin / owner of this calendarlooking at his calendar and can not admin the other cal
if ( $item->class == 'CONFIDENTIAL' ) {
$ical = new iCalendar( array( "icalendar" => $caldav_data) );
// if the event is confidential we fake one that just says "Busy"
$displayname = translate("Busy");
$ical = new iCalendar( array( "icalendar" => $item->caldav_data) );
$ical->Put( 'SUMMARY', $displayname );
$caldav_data = $ical->render(true, $item->caldav_type, $ical->DefaultPropertyList() );
$confidential = new iCalendar( array(
'SUMMARY' => translate('Busy'), 'CLASS' => 'CONFIDENTIAL',
'DTSTART' => $ical->Get('DTSTART'),
'RRULE' => $ical->Get('RRULE')
) );
$duration = $ical->Get('DURATION');
if ( isset($duration) && $duration != "" ) {
$confidential->Set('DURATION', $duration );
}
else {
$confidential->Set('DTEND', $ical->Get('DTEND') );
}
$caldav_data = $confidential->Render( true, $caldav_type );
}
elseif ( $c->hide_alarm ) {
// Otherwise we hide the alarms (if configured to)
$ical = new iCalendar( array( "icalendar" => $item->caldav_data) );
$caldav_data = $ical->render(true, $item->caldav_type, $ical->DefaultPropertyList() );
$ical = new iCalendar( array( "icalendar" => $caldav_data) );
$ical->component->ClearComponents('VALARM');
$caldav_data = $ical->render(true, $caldav_type );
}
}
}
$url = $c->protocol_server_port_script . $item->dav_name;
$url = ConstructURL($item->dav_name);
$prop = new XMLElement("prop");
foreach( $properties AS $k => $v ) {
switch( $k ) {
case 'GETCONTENTLENGTH':
$contentlength = strlen($item->caldav_data);
$contentlength = strlen($caldav_data);
$prop->NewElement("getcontentlength", $contentlength );
break;
case 'CALENDAR-DATA':

View File

@ -2,12 +2,12 @@
include("page-header.php");
echo <<<EOBODY
<h1>RSCDS Not Configured</h1>
<h1>DAViCal 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
<p>There is no configuration file present in <b>/etc/davical/$_SERVER[SERVER_NAME]-conf.php</b> so
your installation is not fully set up.</p>
<h2>The Good News</h2>
<p>Well, you're seeing this! At least you have RSCDS <i>installed</i> :-) You also have Apache and PHP working
<p>Well, you're seeing this! At least you have DAViCal <i>installed</i> :-) You also have Apache and PHP working
and so really you are well on the road to success!</p>
<h2>The Dubious News</h2>
<p>You could try and <a href="http://$_SERVER[SERVER_NAME]/docs/rscds/configuring.html">click here</a> and
@ -17,18 +17,16 @@ include("page-header.php");
<p>The configuration file should look something like this:</p>
<pre>
&lt;?php
// \$c->domain_name = 'rscds.example.com';
// \$c->sysabbr = 'rscds';
// \$c->system_name = 'Really Simple CalDAV Store';
// \$c->domain_name = 'davical.example.com';
// \$c->sysabbr = 'davical';
// \$c->system_name = 'DAViCal CalDAV Server';
\$c->admin_email = 'admin@example.com';
\$c->pg_connect[] = 'dbname=rscds port=5432 user=general';
\$c->pg_connect[] = 'dbname=davical port=5432 user=general';
?&gt;
</pre>
<p>The only really <em>essential</em> thing there is that connect string for the database, although
configuring someone for the admin e-mail is a really good idea.</p>
EOBODY;
include("page-footer.php");
?>

View File

@ -2,14 +2,15 @@
/**
* Manages LDAP repository connection
*
* @package rscds
* @package davical
* @category Technical
* @subpackage caldav
* @subpackage ldap
* @author Maxime Delorme <mdelorme@tennaxia.net>
* @copyright Maxime Delorme
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
require_once("auth-functions.php");
class ldapDrivers
{
@ -49,14 +50,19 @@ class ldapDrivers
$this->valid=false;
return ;
}
if ($port) $this->connect=ldap_connect($host, $port);
else $this->connect=ldap_connect($host);
if ($port)
$this->connect=ldap_connect($host, $port);
else
$this->connect=ldap_connect($host);
if (! $this->connect){
$c->messages[] = sprintf(i18n( "drivers_ldap : Unable to connect to LDAP with port %s on host %s"), $port,$host );
$this->valid=false;
return ;
}
dbg_error_log( "LDAP", "drivers_ldap : Connected to LDAP server %s",$host );
//Set LDAP protocol version
if (isset($config['protocolVersion'])) ldap_set_option($this->connect,LDAP_OPT_PROTOCOL_VERSION, $config['protocolVersion']);
@ -80,20 +86,20 @@ class ldapDrivers
/**
* Retrieve all users from the LDAP directory
*/
function getAllUsers($attributs){
function getAllUsers($attributes){
global $c;
$entry = ldap_list($this->connect,$this->baseDNUsers,$this->filterUsers,$attributs);
$entry = ldap_list($this->connect,$this->baseDNUsers,$this->filterUsers,$attributes);
if (!ldap_first_entry($this->connect,$entry))
$c->messages[] = sprintf(i18n("Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"),$this->filterUsers,join(', ',$attributs), $this->baseDNUsers);
$c->messages[] = sprintf(i18n("Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"),$this->filterUsers,join(', ',$attributes), $this->baseDNUsers);
for($i=ldap_first_entry($this->connect,$entry);
$i&&$arr=ldap_get_attributes($this->connect,$i);
$i=ldap_next_entry($this->connect,$i)
)
{
for($j=0;$j<$arr['count'];$j++){
$row[$arr[$j]] = $arr[$arr[$j]][0];
}
$ret[]=$row;
for($j=0;$j<$arr['count'];$j++){
$row[$arr[$j]] = $arr[$arr[$j]][0];
}
$ret[]=$row;
}
return $ret;
}
@ -102,30 +108,36 @@ class ldapDrivers
* Returns the result of the LDAP query
*
* @param string $filter The filter used to search entries
* @param array $attributs Attributes to be returned
* @param array $attributes Attributes to be returned
* @param string $passwd password to check
* @return array Contains selected attributes from all entries corresponding to the given filter
*/
function requestUser($filter,$attributs=NULL,$passwd)
{
function requestUser( $filter, $attributes=NULL, $passwd) {
$entry=NULL;
// We get the DN of the USER
$entry = ldap_search($this->connect,$this->baseDNUsers,$filter,$attributs);
if ( !ldap_first_entry($this->connect,$entry) ){
dbg_error_log( "ERROR", "drivers_ldap : Unable to find the user with filter %s",$filter );
return false;
}
$dnUser = ldap_get_dn($this->connect, ldap_first_entry($this->connect,$entry));
if(!@ldap_bind($this->connect,$dnUser,$passwd))
return false;
$entry=NULL;
// We get the DN of the USER
$entry = ldap_search($this->connect, $this->baseDNUsers, $filter,$attributes);
if ( !ldap_first_entry($this->connect, $entry) ){
dbg_error_log( "ERROR", "drivers_ldap : Unable to find the user with filter %s",$filter );
return false;
} else {
dbg_error_log( "LDAP", "drivers_ldap : Found a user using filter %s",$filter );
}
$i=ldap_first_entry($this->connect,$entry);
$arr=ldap_get_attributes($this->connect,$i);
for($i=0;$i<$arr['count'];$i++){
$ret[$arr[$i]]=$arr[$arr[$i]][0];
}
return $ret;
$dnUser = ldap_get_dn($this->connect, ldap_first_entry($this->connect,$entry));
if ( !@ldap_bind($this->connect, $dnUser, $passwd) ) {
dbg_error_log( "LDAP", "drivers_ldap : Failed to bind to user %s using password %s", $dnUser, $passwd );
return false;
}
dbg_error_log( "LDAP", "drivers_ldap : Bound to user %s using password %s", $dnUser, $passwd );
$i = ldap_first_entry($this->connect,$entry);
$arr = ldap_get_attributes($this->connect,$i);
for( $i=0; $i<$arr['count']; $i++ ) {
$ret[$arr[$i]]=$arr[$arr[$i]][0];
}
return $ret;
}
}
@ -146,70 +158,106 @@ function getStaticLdap() {
return $ldapDrivers;
}
/**
* Synchronise a cached user with one from LDAP
* @param object $usr A user record to be updated (or created)
*/
function sync_user_from_LDAP( &$usr, $mapping, $ldap_values ) {
global $c;
dbg_error_log( "LDAP", "Going to sync the user from LDAP" );
$validUserFields = get_fields('usr');
foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
if ( isset($validUserFields[$field]) ) {
$usr->{$field} = $value;
dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
}
}
foreach ( $mapping as $field => $value ) {
dbg_error_log( "LDAP", "Considering copying %s", $field );
if ( isset($validUserFields[$field]) ) {
$usr->{$field} = $ldap_values[$value];
dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
}
}
UpdateUserFromExternal( $usr );
}
/**
* Check the username / password against the LDAP server
*/
function LDAP_check($username, $password ){
global $c;
$ldapDriver = getStaticLdap();
if ( $ldapDriver->valid ) {
$mapping = $c->authenticate_hook['config']['mapping_field'];
$attributs=array_values($mapping);
// If the config contains a filter that starts with a ( then believe
// them and don't modify it, otherwise wrap the filter.
$filter_munge = "";
if ( preg_match( '/^\(/', $ldapDriver->filterUsers ) ) {
$filter_munge = $ldapDriver->filterUsers;
} else {
$filter_munge = "($ldapDriver->filterUsers)";
}
$filter="(&$filter_munge(".$mapping["username"]."=$username))";
dbg_error_log( "LDAP", "checking user %s for password %s against LDAP",$username,$password );
$valid = $ldapDriver->requestUser($filter,$attributs,$password);
//is a valid user or not
if ( !$valid ) return false;
$ldap_timestamp = $valid[$mapping["updated"]];
foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
$$k = substr($ldap_timestamp,$v[0],$v[1]);
//ok it is valid is already exist in db ?
$ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
if ( $usr = getUserByName($username) ){
//should we update it ?
$db_timestamp = $usr->updated;
$db_timestamp = substr(strtr($db_timestamp, array(':' => '',' '=>'','-'=>'')),0,14);
if($ldap_timestamp <= $db_timestamp){
return $usr;//no need to update
}
//we should update the user record
}
//it doesn't exist so we create the new user or if we should be updated the user record
require_once("RSCDSUser.php");
$user_no = ( isset($usr->user_no) ? $usr->user_no:0);
$user = new RSCDSUser($user_no);
$validUserField = array_keys($user->Fields);
foreach($c->authenticate_hook['config']['default_value'] as $field => $value)
if(in_array($field,$validUserField)) $user->Set($field, $value);
foreach($mapping as $field => $value)
if(in_array($field,$validUserField)) $user->Set($field, $valid[$value]);
$user->Set("updated", "$Y-$m-$d $H:$M:$S");
$user->write();
if ( !$ldapDriver->valid ) {
dbg_error_log( "ERROR", "Couldn't contact LDAP server for authentication" );
return false;
}
if ( $return = getUserByName($username) ){
return $return;
$mapping = $c->authenticate_hook['config']['mapping_field'];
$attributes = array_values($mapping);
/**
* If the config contains a filter that starts with a ( then believe
* them and don't modify it, otherwise wrap the filter.
*/
$filter_munge = "";
if ( preg_match( '/^\(/', $ldapDriver->filterUsers ) ) {
$filter_munge = $ldapDriver->filterUsers;
}
else {
$filter_munge = "($ldapDriver->filterUsers)";
}
$filter = "(&$filter_munge(".$mapping["username"]."=$username))";
dbg_error_log( "LDAP", "checking user %s for password %s against LDAP",$username,$password );
$valid = $ldapDriver->requestUser( $filter, $attributes, $password );
// is a valid user or not
if ( !$valid ) {
dbg_error_log( "LDAP", "user %s is not a valid user",$username );
return false;
}
$ldap_timestamp = $valid[$mapping["updated"]];
/**
* This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
*/
foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
$$k = substr($ldap_timestamp,$v[0],$v[1]);
$ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
$valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
if ( $usr = getUserByName($username) ) {
// should we update it ?
$db_timestamp = $usr->updated;
$db_timestamp = substr(strtr($db_timestamp, array(':' => '',' '=>'','-'=>'')),0,14);
if($ldap_timestamp <= $db_timestamp) {
return $usr; // no need to update
}
// we will need to update the user record
}
else {
dbg_error_log( "LDAP", "user %s doesn't exist in local DB, we need to create it",$username );
$usr = (object) array( 'user_no' => 0 );
}
// The local cached user doesn't exist, or is older, so we create/update their details
sync_user_from_LDAP($usr, $mapping, $valid );
return $usr;
}
/**
* sync LDAP against the DB
*/
@ -218,80 +266,86 @@ function sync_LDAP(){
$ldapDriver = getStaticLdap();
if($ldapDriver->valid){
$mapping = $c->authenticate_hook['config']['mapping_field'];
$attributs=array_values($mapping);
$ldap_users_tmp = $ldapDriver->getAllUsers($attributs);
$attributes = array_values($mapping);
$ldap_users_tmp = $ldapDriver->getAllUsers($attributes);
foreach($ldap_users_tmp as $key => $ldap_user){
$ldap_users_info[$ldap_user[$mapping["username"]]] = $ldap_user;
unset($ldap_users_tmp[$key]);
}
$qry = new PgQuery( "SELECT username ,user_no, updated FROM usr ");
$qry = new PgQuery( "SELECT username, user_no, updated FROM usr ");
$qry->Exec('sync_LDAP',__LINE__,__FILE__);
while($db_user = $qry->Fetch(true)){
$db_users[] = $db_user['username'];
$db_users_info[$db_user['username']] = array('user_no' => $db_user['user_no'], 'updated' => $db_user['updated']);
}
require_once("RSCDSUser.php");
include_once("RSCDSUser.php");
$ldap_users = array_keys($ldap_users_info);
//users only in ldap
// users only in ldap
$users_to_create = array_diff($ldap_users,$db_users);
//users only in db
$users_to_desactivate = array_diff($db_users,$ldap_users);
//users present in ldap and in the db
// users only in db
$users_to_deactivate = array_diff($db_users,$ldap_users);
// users present in ldap and in the db
$users_to_update = array_intersect($db_users,$ldap_users);
//creation of all users;
if(sizeof($users_to_create)) $c->messages[] = sprintf(i18n('- creating record for users : %s'),join(', ',$users_to_create));
foreach($users_to_create as $username){
$valid=$ldap_users_info[$username];
$ldap_timestamp = $valid[$mapping["updated"]];
foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
$$k = substr($ldap_timestamp,$v[0],$v[1]);
$user = new RSCDSUser(0);
$validUserField = array_keys($user->Fields);
foreach($c->authenticate_hook['config']['default_value'] as $field => $value)
if(in_array($field,$validUserField)) $user->Set($field, $value);
foreach($mapping as $field => $value)
if(in_array($field,$validUserField)) $user->Set($field, $valid[$value]);
$user->Set("updated", "$Y-$m-$d $H:$M:$S");
$messages = $c->messages;
$user->write();
$c->messages = $messages;
// creation of all users;
if ( sizeof($users_to_create) ) {
$c->messages[] = sprintf(i18n('- creating record for users : %s'),join(', ',$users_to_create));
foreach( $users_to_create as $username ) {
$user = (object) array( 'user_no' => 0, 'username' => $username );
$valid = $ldap_users_info[$username];
$ldap_timestamp = $valid[$mapping["updated"]];
/**
* This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
*/
foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
$$k = substr($ldap_timestamp,$v[0],$v[1]);
$ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
$valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
sync_user_from_LDAP( $user, $mapping, $valid );
}
}
//desactivating all users
if(sizeof($users_to_desactivate)){
foreach( $users_to_desactivate AS $v ) {
// deactivating all users
if ( sizeof($users_to_deactivate) ) {
foreach( $users_to_deactivate AS $v ) {
$usr_in .= ', ' . qpg($v);
}
$usr_in = substr($usr_in,1);
$c->messages[] = sprintf(i18n('- desactivating users : %s'),$usr_in);
$qry = new PgQuery( "UPDATE usr set active = FALSE WHERE lower(username) in ($usr_in)");
$c->messages[] = sprintf(i18n('- deactivating users : %s'),$usr_in);
$qry = new PgQuery( "UPDATE usr SET active = FALSE WHERE lower(username) IN ($usr_in)");
$qry->Exec('sync_LDAP',__LINE__,__FILE__);
}
//updating all users
foreach($users_to_update as $key=> $username){
$valid=$ldap_users_info[$username];
$ldap_timestamp = $valid[$mapping["updated"]];
foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
$$k = substr($ldap_timestamp,$v[0],$v[1]);
$ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
$db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '',' '=>'','-'=>'')),0,14);
if($ldap_timestamp > $db_timestamp){
$user = new RSCDSUser($db_users_info[$username]['user_no']);
$validUserField = array_keys($user->Fields);
foreach($c->authenticate_hook['config']['default_value'] as $field => $value)
if(in_array($field,$validUserField)) $user->Set($field, $value);
foreach($mapping as $field => $value)
if(in_array($field,$validUserField)) $user->Set($field, $valid[$value]);
$user->Set("updated", "$Y-$m-$d $H:$M:$S");
$user->write();
} else {
unset($users_to_update[$key]);
$users_nothing_done[] = $username;
// updating all users
if ( sizeof($users_to_update) ) {
foreach ( $users_to_update as $key=> $username ) {
$valid=$ldap_users_info[$username];
$ldap_timestamp = $valid[$mapping["updated"]];
/**
* This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
*/
foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
$$k = substr($ldap_timestamp,$v[0],$v[1]);
$ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
$valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
$db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '',' '=>'','-'=>'')),0,14);
if ( $ldap_timestamp > $db_timestamp ) {
sync_user_from_LDAP($usr, $mapping, $valid );
}
else {
unset($users_to_update[$key]);
$users_nothing_done[] = $username;
}
}
$c->messages[] = sprintf(i18n('- updating user record %s'),join(', ',$users_to_update));
if ( sizeof($users_nothing_done) )
$c->messages[] = sprintf(i18n('- nothing done on %s'),join(', ', $users_nothing_done));
}
if(sizeof($users_to_update)) $c->messages[] = sprintf(i18n('- updating user record %s'),join(', ',$users_to_update));
if(sizeof($users_nothing_done))$c->messages[] = sprintf(i18n('- nothing done on %s'),join(', ', $users_nothing_done));
}
}
?>

83
inc/drivers_squid_pam.php Normal file
View File

@ -0,0 +1,83 @@
<?php
/**
* Manages PAM repository connection with SQUID help
*
* @package davical
* @category Technical
* @subpackage ldap
* @author Eric Seigne <eric.seigne@ryxeo.com>
* @copyright Eric Seigne
* @license http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
require_once("auth-functions.php");
class squidPamDrivers
{
/**#@+
* @access private
*/
/**#@-*/
/**
* Constructor.
* @param string $config path where /usr/lib/squid/pam_auth is
*/
function squidPamDrivers($config){
$this->__construct($config);
}
/**
* The constructor
*
* @param string $config path where /usr/lib/squid/pam_auth is
*/
function __construct($config)
{
global $c;
if (! file_exists($config)){
$c->messages[] = sprintf(i18n( "drivers_squid_pam : Unable to find %s file"), $config );
$this->valid=false;
return ;
}
}
}
/**
* Check the username / password against the PAM system
*/
function SQUID_PAM_check($username, $password ){
global $c;
$cmd = "echo '" . $username . "' '" . $password . "' | " . $c->authenticate_hook['config']['script'] . " -n common-auth";
$auth_result = exec($cmd);
if ( $auth_result == "OK") {
if ( $usr = getUserByName($username) ) {
return $usr;
}
else {
dbg_error_log( "PAM", "user %s doesn't exist in local DB, we need to create it",$username );
$fullname = trim( exec("getent passwd | grep ^" . $username ." | cut -d \":\" -f5"), ' ,' );
$usr = (object) array(
'user_no' => 0,
'username' => $username,
'active' => 't',
'email' => $username . "@" . $c->authenticate_hook['config']['email_base'],
'updated' => date(),
'fullname' => $fullname
);
UpdateUserFromExternal( $usr );
return $usr;
}
}
else {
dbg_error_log( "PAM", "User %s is not a valid username (or password was wrong)", $username );
return false;
}
}

View File

@ -73,11 +73,13 @@ foreach( $busy_tentative AS $k => $v ) {
while ( $date = $rrule->GetNext() ) {
if ( ! $date->GreaterThan($start) ) continue;
if ( $date->GreaterThan($finish) ) break;
$freebusy .= sprintf("FREEBUSY;FBTYPE=BUSY-TENTATIVE:%s/%s\n", $date->Render('Ymd\THis'), $duration );
$todate = clone($date);
$todate->AddDuration($duration);
$freebusy .= sprintf("FREEBUSY;FBTYPE=BUSY-TENTATIVE:%s/%s\n", $date->Render('Ymd\THis'), $todate->Render('Ymd\THis') );
}
}
else {
$freebusy .= sprintf("FREEBUSY;FBTYPE=BUSY-TENTATIVE:%s/%s\n", $start->Render('Ymd\THis'), $duration );
$freebusy .= sprintf("FREEBUSY;FBTYPE=BUSY-TENTATIVE:%s/%s\n", $v->start, $v->finish );
}
}
@ -89,11 +91,13 @@ foreach( $busy AS $k => $v ) {
while ( $date = $rrule->GetNext() ) {
if ( ! $date->GreaterThan($start) ) continue;
if ( $date->GreaterThan($finish) ) break;
$freebusy .= sprintf("FREEBUSY:%s/%s\n", $date->Render('Ymd\THis'), $duration );
$todate = clone($date);
$todate->AddDuration($duration);
$freebusy .= sprintf("FREEBUSY:%s/%s\n", $date->Render('Ymd\THis'), $todate->Render('Ymd\THis') );
}
}
else {
$freebusy .= sprintf("FREEBUSY:%s/%s\n", $start->Render('Ymd\THis'), $duration );
$freebusy .= sprintf("FREEBUSY:%s/%s\n", $v->start, $v->finish );
}
}

View File

@ -5,14 +5,14 @@ $page_menu->AddOption(translate("Home"),"$c->base_url/index.php",translate("Brow
if ( $session->AllowedTo("Admin" )) {
// $page_menu->AddOption(translate("Setup"),"$c->base_url/setup.php",translate("Setup RSCDS"), false, 5000 );
$page_menu->AddOption(translate("Operations"),"$c->base_url/tools.php",translate("Operations on your calendar"), false, 5200 );
$relationship_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
}
$page_menu->AddOption(translate("Logout"),"$c->base_url/index.php?logout",translate("Log out of the").$c->system_name, false, 5400 );
$page_menu->AddOption(translate("Help"),"$c->base_url/help.php",translate("Help on something or other"), false, 8500 );
$page_menu->AddOption(translate("Report Bug"),"http://sourceforge.net/tracker/?func=add&group_id=179845&atid=890785",translate("Report a bug in the system"), false, 9000 );
$relationship_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
$user_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
// $role_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
$user_menu->AddOption(translate("My Details"),"$c->base_url/usr.php?user_no=$session->user_no",translate("View my own user record"), false, 700);

View File

@ -69,8 +69,10 @@ EOHDR;
echo "<div id=\"pageheader\">\n";
if ( isset($page_menu) && is_object($page_menu) ) {
$page_menu->AddSubMenu( $relationship_menu, translate("Relationships"),
if ( isset($relationship_menu) && is_object($relationship_menu) ) {
$page_menu->AddSubMenu( $relationship_menu, translate("Relationships"),
"$c->base_url/relationship_types.php", translate("Browse all relationship types"), false, 4050 );
}
$page_menu->AddSubMenu( $user_menu, translate("Users"), "$c->base_url/users.php", translate("Browse all users"), false, 4100 );
// $page_menu->AddSubMenu( $role_menu, "Roles", "/roles.php", "Browse all roles", false, 4300 );
$page_menu->MakeSomethingActive($active_menu_pattern);

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: RSCDS 0.2.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: 2006-11-06 17:24+1300\n"
"Last-Translator: Cristina Radalescu <andrew@mcmillan.net.nz>\n"
"MIME-Version: 1.0\n"
@ -19,9 +19,9 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgstr ""
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "Benutzerdaten bearbeiten"
#, c-format
msgid "- nothing done on %s"
@ -161,6 +161,10 @@ msgstr "Datenbank Error"
msgid "Date Style"
msgstr "Datumsformat"
#, fuzzy
msgid "Default relationships added."
msgstr "Verhältnis hinzugefuegt"
msgid "Delete"
msgstr "Löschen"
@ -196,7 +200,7 @@ msgid "Enter your username and password then click here to log in."
msgstr ""
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -562,6 +566,10 @@ msgstr ""
msgid "You may not access that calendar"
msgstr ""
#, fuzzy
msgid "You may not access that collection"
msgstr "Sie sind nicht berechtigt diese Funktion zu benutzen"
#, fuzzy
msgid "You may not add entries to this calendar."
msgstr "Sie sind nicht berechtigt diese Funktion zu benutzen"
@ -602,6 +610,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -21,7 +21,7 @@ msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgid "- deactivating users : %s"
msgstr ""
#, c-format
@ -153,6 +153,9 @@ msgstr ""
msgid "Date Style"
msgstr ""
msgid "Default relationships added."
msgstr ""
msgid "Delete"
msgstr ""
@ -188,7 +191,7 @@ msgid "Enter your username and password then click here to log in."
msgstr ""
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -549,6 +552,9 @@ msgstr ""
msgid "You may not access that calendar"
msgstr ""
msgid "You may not access that collection"
msgstr ""
msgid "You may not add entries to this calendar."
msgstr ""
@ -588,6 +594,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: RSCDS 0.2.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: 2006-11-06 17:12+1300\n"
"Last-Translator: Lorena Paoletti <andrew@mcmillan.net.nz>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -20,9 +20,9 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgstr ""
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "Mostrar el registro de éste usuario"
#, c-format
msgid "- nothing done on %s"
@ -162,6 +162,10 @@ msgstr "Error en la Base de Datos."
msgid "Date Style"
msgstr "Formato de Fecha"
#, fuzzy
msgid "Default relationships added."
msgstr "Relación incorporada."
msgid "Delete"
msgstr "Borrar"
@ -197,7 +201,7 @@ msgid "Enter your username and password then click here to log in."
msgstr ""
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -560,6 +564,10 @@ msgstr ""
msgid "You may not access that calendar"
msgstr ""
#, fuzzy
msgid "You may not access that collection"
msgstr "No está autorizado a utilizar ésta función."
#, fuzzy
msgid "You may not add entries to this calendar."
msgstr "No está autorizado a utilizar ésta función."
@ -600,6 +608,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: RSCDS 0.2.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: 2006-11-06 17:12+1300\n"
"Last-Translator: Lorena Paoletti <andrew@mcmillan.net.nz>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -20,9 +20,9 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgstr ""
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "Mostrar el registro de éste usuario"
#, c-format
msgid "- nothing done on %s"
@ -162,6 +162,10 @@ msgstr "Error en la Base de Datos."
msgid "Date Style"
msgstr "Formato de Fecha"
#, fuzzy
msgid "Default relationships added."
msgstr "Relación incorporada."
msgid "Delete"
msgstr "Borrar"
@ -197,7 +201,7 @@ msgid "Enter your username and password then click here to log in."
msgstr ""
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -560,6 +564,10 @@ msgstr ""
msgid "You may not access that calendar"
msgstr ""
#, fuzzy
msgid "You may not access that collection"
msgstr "No está autorizado a utilizar ésta función."
#, fuzzy
msgid "You may not add entries to this calendar."
msgstr "No está autorizado a utilizar ésta función."
@ -600,6 +608,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: RSCDS 0.2.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: 2006-11-06 17:12+1300\n"
"Last-Translator: Lorena Paoletti <andrew@mcmillan.net.nz>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -20,9 +20,9 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgstr ""
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "Mostrar el registro de éste usuario"
#, c-format
msgid "- nothing done on %s"
@ -162,6 +162,10 @@ msgstr "Error en la Base de Datos."
msgid "Date Style"
msgstr "Formato de Fecha"
#, fuzzy
msgid "Default relationships added."
msgstr "Relación incorporada."
msgid "Delete"
msgstr "Borrar"
@ -197,7 +201,7 @@ msgid "Enter your username and password then click here to log in."
msgstr ""
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -560,6 +564,10 @@ msgstr ""
msgid "You may not access that calendar"
msgstr ""
#, fuzzy
msgid "You may not access that collection"
msgstr "No está autorizado a utilizar ésta función."
#, fuzzy
msgid "You may not add entries to this calendar."
msgstr "No está autorizado a utilizar ésta función."
@ -600,6 +608,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: rscds 0.3.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: 2006-11-13 13:03+1300\n"
"Last-Translator: maxime delorme <mdelorme@tennaxia.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -20,8 +20,8 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr "- création des utilisateurs : %s"
#, c-format
msgid "- desactivating users : %s"
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "- desactivation des utilisateurs : %s"
#, c-format
@ -165,6 +165,10 @@ msgstr "Erreur base de données"
msgid "Date Style"
msgstr "Format des dates"
#, fuzzy
msgid "Default relationships added."
msgstr "Relation ajoutée"
msgid "Delete"
msgstr "Supprimer"
@ -203,8 +207,8 @@ msgstr "Tapez un nom pour ce type de ressource"
msgid "Enter your username and password then click here to log in."
msgstr "Tapez votre nom d'utilisateur et votre mot de passe puis cliquez ici pour vous connecter"
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
#, fuzzy, c-format
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr "Erreur NoUserFound avec le filtre >%s<, attributs >%s<, dn >%s<"
msgid "Error querying database."
@ -627,6 +631,10 @@ msgstr "Vous n'avez pas suffisamment d'accès pour verrouiller ça"
msgid "You may not access that calendar"
msgstr "Vous ne pouvez accéder à ce calendrier"
#, fuzzy
msgid "You may not access that collection"
msgstr "Vous ne pouvez accéder à ce calendrier"
msgid "You may not add entries to this calendar."
msgstr "Vous n'êtes pas autorisé à utiliser cette fonction."
@ -670,6 +678,10 @@ msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap mod
msgstr ""
"drivers_ldap : la fonction ldap_connect n'est pas définie, vérifier que vous avez l'extension php_ldap"
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: rscds 0.7.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: 2007-05-03 15:00+0001\n"
"Last-Translator: David Takacs <david.takacs@cafeopen.eu>\n"
"Language-Team: \n"
@ -20,9 +20,9 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgstr ""
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "Felhasználó szerkesztése"
#, c-format
msgid "- nothing done on %s"
@ -155,6 +155,10 @@ msgstr "Adatbázis-hiba"
msgid "Date Style"
msgstr "Dátumformátum"
#, fuzzy
msgid "Default relationships added."
msgstr "Kapcsolat hozzáadva."
msgid "Delete"
msgstr "Törlés"
@ -190,7 +194,7 @@ msgid "Enter your username and password then click here to log in."
msgstr "Írja be a felhasználónevét és jelszavát a belépéshez"
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -553,6 +557,10 @@ msgstr "Nincs jogosultsága zárolni"
msgid "You may not access that calendar"
msgstr "Nincs jogosultsága hozzáférni a naptárhoz"
#, fuzzy
msgid "You may not access that collection"
msgstr "Nincs jogosultsága hozzáférni a naptárhoz"
msgid "You may not add entries to this calendar."
msgstr "Nincs jogosultsága új bejegyzéseket írni a naptárba."
@ -593,6 +601,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -21,7 +21,7 @@ msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgid "- deactivating users : %s"
msgstr ""
#, c-format
@ -158,6 +158,9 @@ msgstr ""
msgid "Date Style"
msgstr ""
msgid "Default relationships added."
msgstr ""
msgid "Delete"
msgstr ""
@ -195,7 +198,7 @@ msgid "Enter your username and password then click here to log in."
msgstr ""
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -562,6 +565,9 @@ msgstr ""
msgid "You may not access that calendar"
msgstr ""
msgid "You may not access that collection"
msgstr ""
msgid "You may not add entries to this calendar."
msgstr ""
@ -602,6 +608,10 @@ msgid ""
"drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a "
"flower) and is an option allowing people to log in automatically in future --"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Eelco Maljaars <http://eelco.maljaars.net/>\n"
"Language-Team: nl_NL <LL@li.org>\n"
@ -20,9 +20,9 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgstr ""
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "Verander deze gebruiker"
#, c-format
msgid "- nothing done on %s"
@ -158,6 +158,10 @@ msgstr "Database fout"
msgid "Date Style"
msgstr "Datum stijl"
#, fuzzy
msgid "Default relationships added."
msgstr "Relatie toegevoegd"
msgid "Delete"
msgstr "Verwijder"
@ -194,7 +198,7 @@ msgid "Enter your username and password then click here to log in."
msgstr "Voer je gebruikersnaam en wachtwoord in en klik hier om in te loggen"
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -557,6 +561,10 @@ msgstr ""
msgid "You may not access that calendar"
msgstr "Je mag die agenda niet benaderen"
#, fuzzy
msgid "You may not access that collection"
msgstr "Je mag die agenda niet benaderen"
msgid "You may not add entries to this calendar."
msgstr "Je mag geen items toevoegen aan deze agenda"
@ -597,6 +605,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: rscds-messages\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: 2007-03-31 00:24+0200\n"
"Last-Translator: Rafal Slubowski <rafalslubowski@gmail.com>\n"
"Language-Team: polski <pl@li.org>\n"
@ -22,9 +22,9 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgstr ""
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "Zmień dane tego użytkownika "
#, c-format
msgid "- nothing done on %s"
@ -157,6 +157,10 @@ msgstr "Błąd bazy danych"
msgid "Date Style"
msgstr "Format daty"
#, fuzzy
msgid "Default relationships added."
msgstr "Zależność została dodana."
msgid "Delete"
msgstr "Usuń"
@ -192,7 +196,7 @@ msgid "Enter your username and password then click here to log in."
msgstr "Wpisz nazwę użytkownika oraz hasło i naciśnij tutaj aby się zalogować."
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -555,6 +559,10 @@ msgstr "Nie masz wystarczających uprawnień aby zarezerwować ten"
msgid "You may not access that calendar"
msgstr "Nie masz dostępu do tego kalendarza"
#, fuzzy
msgid "You may not access that collection"
msgstr "Nie masz dostępu do tego kalendarza"
msgid "You may not add entries to this calendar."
msgstr "Nie możesz dodawać zdarzeń do tego kalendarza."
@ -595,6 +603,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: rscds 0.3.2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-10-16 13:28+1300\n"
"POT-Creation-Date: 2007-11-17 22:06+1300\n"
"PO-Revision-Date: 2006-11-13 23:07+0500\n"
"Last-Translator: Nick Khazov <m2k3d0n@users.sourceforge.net>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -19,9 +19,9 @@ msgstr ""
msgid "- creating record for users : %s"
msgstr ""
#, c-format
msgid "- desactivating users : %s"
msgstr ""
#, fuzzy, c-format
msgid "- deactivating users : %s"
msgstr "Посмотреть мою учетную запись"
#, c-format
msgid "- nothing done on %s"
@ -160,6 +160,10 @@ msgstr ""
msgid "Date Style"
msgstr ""
#, fuzzy
msgid "Default relationships added."
msgstr "Связь добавлена."
msgid "Delete"
msgstr "Удалить"
@ -197,7 +201,7 @@ msgid "Enter your username and password then click here to log in."
msgstr ""
#, c-format
msgid "Error NoUserFound with filter >%s<, attributs >%s< , dn >%s<"
msgid "Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<"
msgstr ""
msgid "Error querying database."
@ -570,6 +574,10 @@ msgstr ""
msgid "You may not access that calendar"
msgstr ""
#, fuzzy
msgid "You may not access that collection"
msgstr "Вы не авторизованы для использоования этой функции."
#, fuzzy
msgid "You may not add entries to this calendar."
msgstr "Вы не авторизованы для использоования этой функции."
@ -610,6 +618,10 @@ msgstr ""
msgid "drivers_ldap : function ldap_connect not defined, check your php_ldap module"
msgstr ""
#, c-format
msgid "drivers_squid_pam : Unable to find %s file"
msgstr ""
msgid ""
"forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option "
"allowing people to log in automatically in future -->"

View File

@ -18,14 +18,14 @@ sed -e 's/CHARSET/UTF-8/' <${PODIR}/messages.tmp >${PODIR}/messages.po
rm ${PODIR}/messages.tmp
for LOCALE in `psql -qAt -c 'SELECT locale FROM supported_locales;' caldav` ; do
for LOCALE in `grep VALUES dba/supported_locales.sql | cut -f2 -d"'"` ; do
if [ ! -f ${PODIR}/${LOCALE}.po ] ; then
cp ${PODIR}/messages.po ${PODIR}/${LOCALE}.po
fi
msgmerge --quiet --width 105 --update ${PODIR}/${LOCALE}.po ${PODIR}/messages.po
done
for LOCALE in `psql -qAt -c 'SELECT locale FROM supported_locales;' caldav` ; do
for LOCALE in `grep VALUES dba/supported_locales.sql | cut -f2 -d"'"` ; do
mkdir -p ${LOCALEDIR}/${LOCALE}/LC_MESSAGES
msgfmt ${PODIR}/${LOCALE}.po -o ${LOCALEDIR}/${LOCALE}/LC_MESSAGES/${APPLICATION}.mo
done

View File

@ -10,11 +10,9 @@ use Getopt::Long qw(:config permute); # allow mixed args.
# Options variables
my $debug = 0;
my $dbname = "rscds";
my $dbport = 5432;
my $dsn = "davical";
my $dbuser = "";
my $dbpass = "";
my $dbhost = "";
my $suite;
my $test;
my $helpmeplease = 0;
@ -25,11 +23,9 @@ my $patchdir = $dbadir . "/patches";
GetOptions ('debug!' => \$debug,
'dbname=s' => \$dbname,
'dsn=s' => \$dsn,
'dbuser=s' => \$dbuser,
'dbpass=s' => \$dbpass,
'dbport=s' => \$dbport,
'dbhost=s' => \$dbhost,
'suite=s' => \$suite,
'case=s' => \$test,
'help' => \$helpmeplease );
@ -40,14 +36,13 @@ usage() if ( $helpmeplease || !defined($suite) || !defined($test));
# Open database connection. Note that the standard PostgreSQL
# environment variables will also work with DBD::Pg.
############################################################
my $dsn = "dbi:Pg:dbname=$dbname";
$dsn .= ";host=$dbhost" if ( "$dbhost" ne "" );
$dsn .= ";port=$dbport" if ( $dbport != 5432 );
my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { AutoCommit => 0 } ) or die "Can't connect to database $dbname";
$dsn = "dbi:Pg:dbname=$dsn";
my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { AutoCommit => 0 } ) or die "Can't connect to database $dsn";
my @arguments = ( "--basic", "--proxy", "", "--silent" );
push @arguments, "--verbose" if ( defined($ARGV[2]) );
my @arguments = ( "--basic", "--proxy", "" );
push @arguments, "--silent" unless ( $debug );
push @arguments, "--verbose" if ( $debug );
my $url;
my $is_head_request = 0;
@ -204,6 +199,7 @@ if ( defined(@{$queries}) && @{$queries} ) {
print STDERR "Processing results row\n" if ( $debug );
my $sep = "";
foreach my $column ( @$row ) {
$column = 'NULL' unless ( defined($column) );
print $sep, $column;
$sep = " --- ";
}
@ -242,11 +238,9 @@ and follow the instructions there.
The following options are available for controlling the database, for
those test cases which might require it:
--dbname <database>
--dsn <database>[;port=NNNN][;host=example.com]
--dbuser <user>
--dbpass <password>
--dbport <port>
--dbhost <host>
The test instructions will include lines defining the test like:

View File

@ -0,0 +1,7 @@
#
# A configuration file for the regression testing.
#
DBNAME=regression
PGPOOL=inactive
HOSTNAME=mycaldav
DSN="regression;port=5432"

View File

@ -2,7 +2,15 @@
#
# Run the regression tests and display differences
#
DBNAME=caldav
DBNAME=regression
PGPOOL=inactive
HOSTNAME=regression
. regression.conf
[ -z "${DSN}" ] && DSN="${DBNAME}"
UNTIL=${1:-"99999"}
ACCEPT_ALL=${2:-""}
@ -40,7 +48,7 @@ drop_database() {
# Restart PGPool to ensure we can drop and recreate the database
# FIXME: We should really drop everything *from* the database and create it
# from that, so we don't need to do this.
sudo /etc/init.d/pgpool restart
[ "${PGPOOL}" = "inactive" ] || sudo /etc/init.d/pgpool restart
dropdb $1
if psql -ltA | cut -f1 -d'|' | grep "^$1$" >/dev/null ; then
echo "Failed to drop $1 database"
@ -69,7 +77,7 @@ for T in ${REGRESSION}/*.test ; do
if [ "${TESTNUM}" -gt "${UNTIL}" ] ; then
break;
fi
./dav_test --dbname caldav --suite regression-suite --case "${TEST}" | ./normalise_result > "${RESULTS}/${TEST}"
./dav_test --dsn "${DSN}" --suite regression-suite --case "${TEST}" | ./normalise_result > "${RESULTS}/${TEST}"
check_result "${TEST}"

132
testing/sniffstream Executable file
View File

@ -0,0 +1,132 @@
#!/usr/bin/perl -w
#
# Sniff traffic and format as a stream of packet contents
#
use strict;
use Getopt::Long qw(:config permute); # allow mixed args.
# Options variables
my $debug = 0;
my $saveto;
my $readfrom;
my $interface = 'any';
my $dumpspec = 'tcp port 80';
my $helpmeplease = 0;
GetOptions ('debug!' => \$debug,
'write=s' => \$saveto,
'file=s' => \$readfrom,
'interface=s' => \$interface,
'dumpspec=s' => \$dumpspec,
'help' => \$helpmeplease );
usage() if ( $helpmeplease );
if ( defined($saveto) ) {
open( SAVETO, '>>', $saveto ) or die "Couldn't save to '$saveto'";
}
if ( defined($readfrom) ) {
if ( $readfrom ne '-' ) {
open( STDIN, '<', $readfrom ) or die "Couldn't open '$readfrom'";
}
}
else {
my @tcpdumpoptions = ('-i', $interface, '-s0', '-l', '-xx', '-n', '-q', $dumpspec );
open( STDIN, '-|', "tcpdump", @tcpdumpoptions ) or die "Couldn't start tcpdump process";
}
my $timestamp;
my $source = '';
my $dest = '';
my $lastsource = '';
my $lastdest = '';
my $show;
my $packet;
my $stream;
while( <STDIN> ) {
$show = 0;
if ( /^([012]\d:[0-5]\d:[0-5]\d\.\d{6})\sIP\s([0-9.:]+)\s>\s([0-9.:]+):\ tcp/ ) {
$timestamp = $1;
$source = $2;
$dest = $3;
}
elsif ( /^\s+(0x....):\s(( [0-9a-f]{4}){1,8})/i ) {
my $pos = hex($1);
my $hex = $2;
next unless defined($hex);
if ( $pos == 64 ) {
$hex = substr( $hex, 10 );
$pos += 4;
}
if ( $pos >= 68 ) {
my @hex = split /\s+/, $hex;
my $ascii = "";
foreach my $xch ( @hex ) {
next if ( $xch eq '' );
$ascii .= chr(hex(substr($xch,0,2)));
$ascii .= chr(hex(substr($xch,2,2)));
}
$show = 1;
$_ = $ascii;
}
}
elsif ( /^\.\./ ) {
s/^\.\.......//;
$show = 1;
}
else {
$show = 1;
}
if ( $show ) {
if ( $source ne $lastsource || $dest ne $lastdest ) {
putline( "\n\n=============== $timestamp $source ==> $dest\n" );
$lastsource = $source;
$lastdest = $dest;
}
putline( $_ );
}
}
###########################################################
sub putline {
my $line = shift;
print $line;
print SAVETO $line if ( defined($saveto) );
}
###########################################################
sub usage {
print <<EOERROR ;
Usage: sniffstream [options]
The sniffstream program will format the output of "tcpdump -s0 -n -q -xx"
for easier reading and comparison, with a view to seeing the actions
involved in a DAV communication session. By default it will run the
tcpdump command internally.
It will also somewhat format the output of "tcpdump -s0 -n -q -A".
Options:
--write <filename> Append the stream to the named file.
--file (-|<filename>) Format the input from the named file, or stdin.
--interface <ifname> Run tcpdump against the specified interface.
--dumpspec <spec> Run tcpdump with that capture specification .
The default interface is 'any' and the default dumpspec is 'tcp port 80'.
EOERROR
exit 1;
}

View File

@ -1,7 +1,7 @@
HTTP/1.1 200 OK
Date: Dow, 01 Jan 2000 00:00:00 GMT
Allow: OPTIONS, GET, HEAD, PUT, DELETE, PROPFIND, MKCOL, MKCALENDAR, LOCK, UNLOCK, REPORT, PROPPATCH
DAV: 1, 2, access-control, calendar-access
Allow: OPTIONS, GET, HEAD, PROPFIND, REPORT
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
ETag: "373430133d9c51260783bc61177aa1d3"
Content-Length: 1880
Content-Type: text/xml; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
ETag: "cc0f368288b5b8373d00e28f842190e6"
Content-Length: 383
Content-Type: text/xml; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 201 Created
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Cache-Control: no-cache
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,11 +1,12 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
ETag: "4bbc059be0ae780aa041d72b2ee837a6"
Content-Length: 726
DAV: 1, 2, access-control, calendar-access
ETag: "a30e9b8f2662cd1da17252d3b814ef04"
Content-Length: 730
Content-Type: text/xml; charset="utf-8"
<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:">
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<response>
<href>/caldav.php/user1/</href>
<propstat>
@ -28,7 +29,7 @@ Content-Type: text/xml; charset="utf-8"
<getcontentlength/>
<resourcetype>
<collection/>
<calendar xmlns="urn:ietf:params:xml:ns:caldav"/>
<C:calendar/>
</resourcetype>
</prop>
<status>HTTP/1.1 200 OK</status>

View File

@ -1,11 +1,12 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
ETag: "63c4632cd1f99190e4613ed7912bc44a"
Content-Length: 483
DAV: 1, 2, access-control, calendar-access
ETag: "7364b32495feb0c9bbe89cdb10988762"
Content-Length: 487
Content-Type: text/xml; charset="utf-8"
<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:">
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<response>
<href>/caldav.php/user1/home/</href>
<propstat>
@ -14,7 +15,7 @@ Content-Type: text/xml; charset="utf-8"
<getcontentlength/>
<resourcetype>
<collection/>
<calendar xmlns="urn:ietf:params:xml:ns:caldav"/>
<C:calendar/>
</resourcetype>
<getetag>"faf25336de0e470a54075c14cbcf5272"</getetag>
</prop>

View File

@ -1,5 +1,6 @@
HTTP/1.1 405 Method Not Allowed
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Content-Length: 45
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 201 Created
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
ETag: "b000d7defa19ccb7cd21e546b54155ee"
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 204 No Content
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
ETag: "b000d7defa19ccb7cd21e546b54155ee"
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 201 Created
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
ETag: "22158fc45876987b2b00749a3a1684d8"
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,11 +1,12 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
ETag: "3f528a647491c5f1bd3e5ac60dc7d471"
Content-Length: 1051
DAV: 1, 2, access-control, calendar-access
ETag: "4cd4df7d08592927b92bcd697c2cdde7"
Content-Length: 1055
Content-Type: text/xml; charset="utf-8"
<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:">
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<response>
<href>/caldav.php/user1/home/</href>
<propstat>
@ -14,7 +15,7 @@ Content-Type: text/xml; charset="utf-8"
<getcontentlength>1425</getcontentlength>
<resourcetype>
<collection/>
<calendar xmlns="urn:ietf:params:xml:ns:caldav"/>
<C:calendar/>
</resourcetype>
</prop>
<status>HTTP/1.1 200 OK</status>

View File

@ -1,5 +1,6 @@
HTTP/1.1 204 No Content
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
ETag: "2c32a2f8aba853654eb17fe037a4db4d"
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,11 +1,12 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
ETag: "c195de52b2e0c2e02fb1da08051bde14"
Content-Length: 1051
DAV: 1, 2, access-control, calendar-access
ETag: "d880baf3102a9de9bcea4d5f71a6b500"
Content-Length: 1055
Content-Type: text/xml; charset="utf-8"
<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:">
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<response>
<href>/caldav.php/user1/home/</href>
<propstat>
@ -14,7 +15,7 @@ Content-Type: text/xml; charset="utf-8"
<getcontentlength>1467</getcontentlength>
<resourcetype>
<collection/>
<calendar xmlns="urn:ietf:params:xml:ns:caldav"/>
<C:calendar/>
</resourcetype>
</prop>
<status>HTTP/1.1 200 OK</status>

View File

@ -1,5 +1,6 @@
HTTP/1.1 201 Created
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Cache-Control: no-cache
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 403 Forbidden
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Content-Length: 36
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 201 Created
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
ETag: "75a75e1c7c4546074aab7645b5323738"
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 400 Bad Request
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Content-Length: 46
Connection: close
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 412 Precondition Failed
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Content-Length: 44
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 204 No Content
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 201 Created
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
ETag: "81979ab45975368d619171a4c3e1e5e2"
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,5 +1,6 @@
HTTP/1.1 204 No Content
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,7 +1,7 @@
HTTP/1.1 200 OK
Date: Dow, 01 Jan 2000 00:00:00 GMT
Allow: OPTIONS, GET, HEAD, PUT, DELETE, PROPFIND, MKCOL, MKCALENDAR, LOCK, UNLOCK, REPORT, PROPPATCH
DAV: 1, 2, access-control, calendar-access
Allow: OPTIONS, GET, HEAD, PUT, DELETE, PROPFIND, MKCOL, MKCALENDAR, LOCK, UNLOCK, REPORT, PROPPATCH
Content-Length: 0
Content-Type: text/plain; charset="utf-8"

View File

@ -1,13 +1,14 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
ETag: "7dd087918d1ab4ba7e7861307b57c6dd"
Content-Length: 341
DAV: 1, 2, access-control, calendar-access
ETag: "5d903e7367fc5fd8975cd2a2f12c94ef"
Content-Length: 326
Content-Type: text/xml; charset="utf-8"
<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:">
<response>
<href>http://mycaldav/caldav.php/user1/home/3F4CF6227300FD062D9EF3CDFB30D32D-0.ics</href>
<href>/caldav.php/user1/home/3F4CF6227300FD062D9EF3CDFB30D32D-0.ics</href>
<propstat>
<prop>
<getetag>"2c32a2f8aba853654eb17fe037a4db4d"</getetag>

View File

@ -1,5 +1,6 @@
HTTP/1.1 200 OK
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, access-control, calendar-access
Etag: "2c32a2f8aba853654eb17fe037a4db4d"
Content-Length: 747
Content-Type: text/calendar

Some files were not shown because too many files have changed in this diff Show More