--- calendarserver-2.4.dfsg.orig/debian/calendarserver-doc-api.docs
+++ calendarserver-2.4.dfsg/debian/calendarserver-doc-api.docs
@@ -0,0 +1 @@
+#doc/apidocs
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.manpages
+++ calendarserver-2.4.dfsg/debian/calendarserver.manpages
@@ -0,0 +1,4 @@
+doc/caldavd.8
+doc/caladmin.8
+doc/calendarserver_export.8
+doc/calendarserver_manage_principals.8
--- calendarserver-2.4.dfsg.orig/debian/copyright
+++ calendarserver-2.4.dfsg/debian/copyright
@@ -0,0 +1,41 @@
+This package was debianized by Guido Guenther <agx@sigxcpu.org> on
+Sun, 27 Aug 2006 19:53:15 +0200.
+
+It was downloaded from http://trac.calendarserver.org/browser/CalendarServer
+
+Upstream Author: Cyrus Daboo <cdaboo@apple.com>
+
+Copyright: 2006 Apple Computer, Inc. All rights reserved.
+
+License:
+
+You are free to distribute this software under the terms of the Apache License
+2.0. The full text of this license can be found in the file
+/usr/share/common-licenses/Apache-2.0.
+
+Except for bin/xattr which is:
+
+##
+# Copyright (c) 2007 Apple Inc.
+#
+# This is the MIT license. This software may also be distributed under the
+# same terms as Python (the PSF license).
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+##
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.postinst
+++ calendarserver-2.4.dfsg/debian/calendarserver.postinst
@@ -0,0 +1,86 @@
+#!/bin/sh
+# postinst script for calendarserver
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+#
+
+case "$1" in
+ configure)
+ if ! getent passwd caldavd >/dev/null 2>&1; then
+ adduser --system --home /var/spool/caldavd --no-create-home \
+ --gecos "calendarserver daemon" --group --disabled-password \
+ caldavd
+ fi
+ adduser caldavd ssl-cert
+
+ for dir in spool run log lib; do
+ if [ ! -d /var/$dir/caldavd ]; then
+ mkdir -p /var/$dir/caldavd
+ fi
+ if ! dpkg-statoverride --list /var/$dir/caldavd >/dev/null 2>&1; then
+ chown caldavd: /var/$dir/caldavd
+ fi
+ done
+
+ echo "Moving data from old DataRoot (/var/run/caldavd/) to new DataRoot (/var/lib/caldavd/) (See Debian Bug#611165 for more info)"
+ for x in calendaruserproxy.sqlite resourceinfo.sqlite mailgatewaytokens.sqlite tasks; do
+ echo -n "Moving /var/run/caldavd/$x ... "
+ if [ -e /var/run/caldavd/$x ]; then
+ if [ ! -e /var/lib/caldavd/$x ]; then
+ mv /var/run/caldavd/$x /var/lib/caldavd/$x
+ echo "Done."
+ else
+ echo "Aborted as /var/lib/caldavd/$x already exists."
+ fi
+ else
+ echo "Aborted as it does not exist."
+ fi
+ done
+
+ for file in calendaruserproxy.sqlite resourceinfo.sqlite mailgatewaytokens.sqlite; do
+ if [ ! -f /var/lib/caldavd/$file ]; then
+ touch /var/lib/caldavd/$file
+ fi
+ if ! dpkg-statoverride --list /var/lib/caldavd/$file >/dev/null 2>&1; then
+ chown caldavd: /var/lib/caldavd/$file
+ chmod 640 /var/lib/caldavd/$file
+ fi
+ done
+
+ # Upgrade NSS users and groups data directories (See Debian Bug#610124)
+ if [ -f /etc/caldavd/caldavd.plist.upgrade.nss.tmp ]; then
+ python /usr/share/doc/calendarserver/scripts/upgrade-nss-data-directories.py
+ rm -f /etc/caldavd/caldavd.plist.upgrade.nss.tmp
+ fi
+ ;;
+ abort-upgrade|abort-remove|abort-deconfigure)
+
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.dirs
+++ calendarserver-2.4.dfsg/debian/calendarserver.dirs
@@ -0,0 +1,4 @@
+/var/lib/caldavd
+/var/log/caldavd
+/var/run/caldavd
+/var/spool/caldavd
--- calendarserver-2.4.dfsg.orig/debian/TODO
+++ calendarserver-2.4.dfsg/debian/TODO
@@ -0,0 +1,2 @@
+* fix apidoc build
+* push pydirector patches upstream
--- calendarserver-2.4.dfsg.orig/debian/control
+++ calendarserver-2.4.dfsg/debian/control
@@ -0,0 +1,24 @@
+Source: calendarserver
+Section: python
+Priority: optional
+Maintainer: Rahul Amaram <amaramrahul@users.sourceforge.net>
+Build-Depends: cdbs (>= 0.4.43), debhelper (>= 5), quilt, python, python-central
+Standards-Version: 3.9.1
+Homepage: http://calendarserver.org
+XS-Python-Version: >= 2.5
+
+Package: calendarserver
+Architecture: all
+Depends: ${python:Depends}, ${misc:Depends}, python-plist,
+ python-kerberos (>= 1.1+svn4241), python-pysqlite2, python-openssl,
+ python-vobject (>= 0.8.1c), python-twisted-calendarserver (>= 8.2.0.svn27622),
+ python-dateutil (>= 1.2),
+ python-xattr,
+ ssl-cert, adduser, lsb-base (>= 3.0-10), memcached (>=1.2.6)
+Recommends: python-pydirector, python-ldap
+XB-Python-Version: ${python:Versions}
+Description: Apple's Calendar Server
+ Apple's Calendarserver is a standalone caldav server with:
+ * Basic or Kerberos Authentication
+ * support for shared calendars
+
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.docs
+++ calendarserver-2.4.dfsg/debian/calendarserver.docs
@@ -0,0 +1 @@
+README
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.preinst
+++ calendarserver-2.4.dfsg/debian/calendarserver.preinst
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+set -e
+
+# Backup configuration file for upgrading NSS users and groups data directories in postinst
+if [ \( "$1" = "install" -o "$1" = "upgrade" \) -a "`echo $2 | cut -d - -f 1`" = "1.2.dfsg" ]; then
+ if [ -f /etc/caldavd/caldavd.plist -a ! -e /etc/caldavd/caldavd.plist.upgrade.nss.tmp ]; then
+ cp -a /etc/caldavd/caldavd.plist /etc/caldavd/caldavd.plist.upgrade.nss.tmp
+ fi
+fi
+
+#DEBHELPER#
--- calendarserver-2.4.dfsg.orig/debian/changelog
+++ calendarserver-2.4.dfsg/debian/changelog
@@ -0,0 +1,322 @@
+calendarserver (2.4.dfsg-6) unstable; urgency=low
+
+ * Changed DataRoot from /var/run/caldavd/ to /var/lib/caldavd/
+ (Closes: Bug#611165)
+
+ -- Rahul Amaram <amaramrahul@users.sourceforge.net> Mon, 31 Jan 2011 17:09:50 +0530
+
+calendarserver (2.4.dfsg-5) unstable; urgency=high
+
+ * Backing up configuration file in calendarserver.preinst was happening
+ only during upgrade. This has been modified to happen during install as
+ well where the configuration file had not been purged during the previous
+ uninstallation.
+
+ -- Rahul Amaram <amaramrahul@users.sourceforge.net> Wed, 19 Jan 2011 14:24:14 +0530
+
+calendarserver (2.4.dfsg-4) unstable; urgency=high
+
+ * Moved the code for upgrading data directories of NSS users and groups
+ into a separate file and executing this file from postinst as it cannot
+ be assumed that python would be configured when preinst is called
+ (inspite of adding python to Pre-Depends)
+ * Moved ${python:Depends} and python-plist from Pre-Depends to Depends
+
+ -- Rahul Amaram <amaramrahul@users.sourceforge.net> Fri, 31 Dec 2010 14:58:23 +0530
+
+calendarserver (2.4.dfsg-3) unstable; urgency=low
+
+ * Added calendarserver.preinst script for moving data directories of NSS users
+ and groups
+ * Moved ${python:Depends} from Depends to Pre-Depends. Added python-plist to
+ Pre-Depends.
+ * Added python-ldap as recommended package (Closes: #600799)
+ * Moved python-pydirector from suggested package to recommended package
+ * Added information about disabling of group calendaring in calendarserver 2.x
+ in NEWS (Closes: #600999)
+
+ -- Rahul Amaram <amaramrahul@users.sourceforge.net> Fri, 17 Dec 2010 00:00:00 +0530
+
+calendarserver (2.4.dfsg-2.1) unstable; urgency=high
+
+ * Non-maintainer upload.
+ * Do not set PYTHONPATH env var, not needed (Closes: #605166, #605157)
+ * Prevent setup.py from automagically setting #PYTHONPATH in an insecure way
+
+ -- Dmitrijs Ledkovs <dmitrij.ledkov@ubuntu.com> Fri, 03 Dec 2010 21:28:40 +0000
+
+calendarserver (2.4.dfsg-2) unstable; urgency=low
+
+ * Removed Uploaders field in debian/changelog
+ * Changed build dependency python-dev to python
+ * Updated standards version to 3.9.1
+ * $remote_fs dependency has been added in init.d
+ * Added NSS directory service backend
+ * Added sample configuration for LDAP directory backend
+ * Added PAM configuration file
+
+ -- Rahul Amaram <amaramrahul@users.sourceforge.net> Wed, 25 Aug 2010 15:23:37 +0530
+
+calendarserver (2.4.dfsg-1) experimental; urgency=low
+
+ * New upstream release (Closes: #579610)
+ * add LDAP directory backend
+ * remove NSS directory backend (will be added soon)
+ * add patch for linux xattr fix. Related Calendarserver ticket:
+ http://trac.calendarserver.org/ticket/337
+
+ -- Rahul Amaram <rahul@amaram.name> Wed, 28 Jul 2010 13:57:56 +0530
+
+calendarserver (1.2.dfsg-9) unstable; urgency=low
+
+ [ Guido Günther ]
+ * [e933bae] Orphan package
+ * [aa801ee] use lower case http in the principals name since this is
+ what iCal expects. clients don't break. (Closes: #514931) - thanks
+ to Arthur P Prokosch for pointing this out and testing that other
+ * [2de5e32] We need both principals in the servers's keytab. (Closes:
+ #514931)
+
+ [ Christoph Goehre ]
+ * [080f99a] remove package depends 'python-xml' python-xml was merged into
+ the python core package and removed from unstable on 16th August 2009
+
+ -- Guido Günther <agx@sigxcpu.org> Sun, 16 May 2010 15:15:51 +0200
+
+calendarserver (1.2.dfsg-8) unstable; urgency=low
+
+ * [8b63fa5] fix basic auth via apache directoryService (Closes:
+ #503727) - thanks to Thomas Viehmann for the patch
+ * [a9f92f3] minor fix: tabs vs. spaces
+
+ -- Guido Günther <agx@sigxcpu.org> Thu, 06 Nov 2008 09:57:56 +0100
+
+calendarserver (1.2.dfsg-7) experimental; urgency=low
+
+ * [972f1c3] add more info about the name service switch backend and
+ warn about ldap server search limits (Closes: #499963)
+ * [45cc01f] add Ticket207-2.patch fixes "Events more than 356 days
+ from creation are ignored" (Closes: #489188) - Patch by Peter Mogensen
+
+ -- Guido Guenther <agx@sigxcpu.org> Thu, 02 Oct 2008 13:18:01 +0200
+
+calendarserver (1.2.dfsg-6) unstable; urgency=low
+
+ * [cf51d29] allow dh_installinit to start/stop the daemon
+ * [f2450f8] don't start calendarserver by default
+ * [67f428c] don't fail when trying to stop a not running daemon
+ * [b3386ab] bump standards version
+ * [da9254c] add README.source
+
+ -- Guido Guenther <agx@sigxcpu.org> Fri, 08 Aug 2008 11:58:23 +0200
+
+calendarserver (1.2.dfsg-5) unstable; urgency=low
+
+ * [b52ad1f] remove dependency on python-plistlib, it's included in included
+ in python2.5 and in Debian's python2.4
+
+ -- Guido Guenther <agx@sigxcpu.org> Fri, 08 Aug 2008 10:36:43 +0200
+
+calendarserver (1.2.dfsg-4) unstable; urgency=low
+
+ * [41eff3c] nss backend: use shortname for groups and users. The groupPrefix
+ makes sure these don't overlap. This way we don't have to use random uids
+ for the calendars on disk which makes administration a lot easier.
+ * [1d6701b] depend on python-openssl instead of the transitional
+ package python-pyopenssl
+ * [d90a173] refer to /usr/share/common-licenses/Apache-2.0 instead of
+ shipping the whole license
+ * [1937567] update upstream URL
+
+ -- Guido Guenther <agx@sigxcpu.org> Sun, 22 Jun 2008 21:05:39 +0200
+
+calendarserver (1.2.dfsg-3) unstable; urgency=low
+
+ [ Guido Guenther ]
+ * upload to unstable - we no longer conflict on twisted
+ * [31d2b55] drop dependency on ctypes - it's already included in python2.5
+ * [652da62] Mention XFS (Closes: #483987) - thanks to Peter Mann
+ * [29edb85] add Homepage:
+ * [0c5bcee] redirect stderr to /dev/null on daemon restart This is a
+ temporary workaround until the twisted deprecation warnings got
+ fixed.
+
+ [ Noel Köthe ]
+ * [1c03474] xs- prefix from Vcs fields
+
+ -- Guido Guenther <agx@sigxcpu.org> Thu, 19 Jun 2008 17:05:15 +0200
+
+calendarserver (1.2.dfsg-2) experimental; urgency=low
+
+ [ Guido Guenther ]
+ * fix epydoc errors
+ * use a python-twisted-calendarserver that doesn't ship the whole twisted
+
+ [ Noel Köthe ]
+ * bind the caldavd only to localhost as described in README.Debian
+
+ -- Guido Guenther <agx@sigxcpu.org> Mon, 05 May 2008 17:28:58 +0200
+
+calendarserver (1.2.dfsg-1) unstable; urgency=low
+
+ [ Guido Guenther ]
+ * New upstream 1.2
+ * add NSS directory backend
+ * bump python-twisted-calendarserver dependency to one that has the patches
+ for 1.2
+ * README.Debian: use non SSL port
+
+ [ Noel Köthe ]
+ * README.Debian:
+ * s/sudoers.xml/sudoers.plist/
+ * correcting path
+ * update TODO
+
+ -- Guido Guenther <agx@sigxcpu.org> Sun, 27 Apr 2008 10:36:57 +0200
+
+calendarserver (1.2.dfsg~dev020221-5) unstable; urgency=low
+
+ * first upload to unstable
+ * tighten dependencies and drop suggests superflous suggests
+ * switch to Python 2.5
+ * README.Debian: add URI for group calendars
+
+ -- Guido Guenther <agx@sigxcpu.org> Sun, 20 Apr 2008 13:59:04 +0200
+
+calendarserver (1.2.dfsg~dev020221-4) experimental; urgency=low
+
+ * update README.Debian on SPNEGO/Kerberos
+ * fix snakeoil certificate paths
+
+ -- Guido Guenther <agx@sigxcpu.org> Sun, 16 Mar 2008 12:48:34 +0100
+
+calendarserver (1.2.dfsg~dev020221-3) experimental; urgency=low
+
+ * repackage upstream branch and remove RFCs too make the document dfsg clean
+ * add license of bin/xattr
+
+ -- Guido Guenther <agx@sigxcpu.org> Wed, 27 Feb 2008 10:31:48 +0100
+
+calendarserver (1.2~dev020221-2) experimental; urgency=low
+
+ * disable the api-doc generation until it works with newer epydoc
+
+ -- Guido Guenther <agx@sigxcpu.org> Tue, 26 Feb 2008 11:01:00 +0100
+
+calendarserver (1.2~dev020221-1) experimental; urgency=low
+
+ * first upload to experimental (Closes: #384644)
+ * switch to upstreams 1.2 development branch
+ * drop krb5 patch, applied upstream
+ * refreh paths.diff
+ * README.Debian: add calendar URI
+ * depend on renamed python-twisted-calendarserver package
+
+ -- Guido Guenther <agx@sigxcpu.org> Thu, 21 Feb 2008 17:54:56 +0100
+
+calendarserver (0.0.svn1755-1) experimental; urgency=low
+
+ * UNRELEASED
+ * New Upstream SVN Snapshot
+ * update dependencies
+ * drop opendirectory-dummy.patch, not needed anymore, can be disabled via
+ the config file
+ * drop caldavd-kerberos.patch, not needed anymore. Kerberos can be enabled
+ via config file now
+ * update paths.diff to new layout and config options
+ * new fix-krb-service.diff: fix kerberos service names
+ * calendarserver.init: daemon switches users itself, new commandline options
+ * set process count to avoid pydirector for now
+
+ -- Guido Guenther <agx@sigxcpu.org> Thu, 09 Aug 2007 10:24:46 +0200
+
+calendarserver (0.0.svn209-2) calendarserver; urgency=low
+
+ * create /var/run/caldavd (needed inc ase /var/run is on a tmpfs)
+ * drop dependencies on python-dateutil and python-zopeinterface since
+ these are indirect dependencies of python-vobject and
+ twisted-calendarserver respectively.
+ * build the api documentation
+
+ -- Guido Guenther <agx@sigxcpu.org> Mon, 2 Oct 2006 11:03:27 +0200
+
+calendarserver (0.0.svn209-1) calendarserver; urgency=low
+
+ * New Upstream Version
+ * depend on newer pykerberos
+ * depend on newer twisted-calendarserver
+
+ -- Guido Guenther <agx@sigxcpu.org> Wed, 27 Sep 2006 12:34:28 +0200
+
+calendarserver (0.0.svn197-1) calendarserver; urgency=low
+
+ * New Upstream Version
+ * depend on newer twisted
+ * twiddle path patch to apply again
+
+ -- Guido Guenther <agx@sigxcpu.org> Tue, 26 Sep 2006 10:21:00 +0200
+
+calendarserver (0.0.svn188-1) unstable; urgency=low
+
+ * New upstream version 0.0.svn188
+ * bump dependencies on python-vobject
+ * depend on python-xml
+ * adjust patch path to look for twisted unter /usr/lib/caldavd
+ since we ship our own twisted version in this subdir now. This way
+ we don't have to conflict with the twisted shipped in Debian.
+ * depend on twisted-calendarserver (which was formerly
+ python-twisted-acl-branch)
+
+ -- Guido Guenther <agx@sigxcpu.org> Mon, 25 Sep 2006 18:33:19 +0200
+
+calendarserver (0.0.svn142-3) unstable; urgency=low
+
+ * add a sample diff for kerberos authentication
+
+ -- Guido Guenther <agx@sigxcpu.org> Tue, 19 Sep 2006 10:06:53 +0200
+
+calendarserver (0.0.svn142-2) unstable; urgency=low
+
+ * depend on python-plistlib
+
+ -- Guido Guenther <agx@sigxcpu.org> Mon, 18 Sep 2006 19:54:24 +0200
+
+calendarserver (0.0.svn142-1) unstable; urgency=low
+
+ * new SVN version (only patches merged into twisted)
+ * depend on more recent twisted-acl-branch
+
+ -- Guido Guenther <agx@sigxcpu.org> Mon, 18 Sep 2006 17:06:15 +0200
+
+calendarserver (0.0.svn135-2) unstable; urgency=low
+
+ * really drop upstream-patches/
+ * fix restart target in init script, use LSB logging
+ * add LSB header
+
+ -- Guido Guenther <agx@sigxcpu.org> Mon, 18 Sep 2006 16:00:45 +0200
+
+calendarserver (0.0.svn135-1) unstable; urgency=low
+
+ * New upstream svn version
+ * no need to rename patches to upstream-patches, upstream changed that to
+ lib-patches
+ * depend on newer twisted-acl-branch and newer pykerberos
+ * remove superflous heimdal-dev from build-depends
+ * dropped patches (applied upstream):
+ * bashism
+ * move-patch-dir
+ * linux-xattr
+ * use snake-oil certificates for SSL
+
+ -- Guido Guenther <agx@sigxcpu.org> Fri, 15 Sep 2006 18:38:10 +0200
+
+calendarserver (0.0.svn62-1) unstable; urgency=low
+
+ * Initial release
+ * Add dummies for OpenDirectory interaction from:
+ http://svn.macosforge.org/projects/calendarserver/browser/PyOpenDirectory
+ until we have a proper port
+
+ -- Guido Guenther <agx@sigxcpu.org> Wed, 6 Sep 2006 13:45:40 +0200
+
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.default
+++ calendarserver-2.4.dfsg/debian/calendarserver.default
@@ -0,0 +1,8 @@
+# Defaults for calendarserver initscript (/etc/init.d/calendarserver)
+# This is a POSIX shell fragment
+
+# uncomment to start calendarserver on system startup
+#start_calendarserver=yes
+
+# options to pass to calendarserver on startup
+#DAEMON_OPTS=""
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.examples
+++ calendarserver-2.4.dfsg/debian/calendarserver.examples
@@ -0,0 +1,5 @@
+conf/caldavd.plist
+conf/sudoers.plist
+conf/auth/accounts.xml
+conf/auth/accounts.htauth
+conf/auth/accounts.htdigest
--- calendarserver-2.4.dfsg.orig/debian/pycompat
+++ calendarserver-2.4.dfsg/debian/pycompat
@@ -0,0 +1 @@
+2
--- calendarserver-2.4.dfsg.orig/debian/README.Debian
+++ calendarserver-2.4.dfsg/debian/README.Debian
@@ -0,0 +1,123 @@
+calendar server for Debian
+==========================
+
+Basic Setup
+===========
+Since calendarserver uses extended attributes you must mount the filesystem
+that contains the calendars (/var/spool/caldavd by default) with extended
+attributes enabled. On ext2/ext3 filesystems use the user_xattr mount option,
+XFS has extended attributes enabled by default.
+
+You have to add a /etc/caldavd/accounts.xml to tell caldavd about your accounts
+and users. See /usr/share/doc/calendarserver/examples/accounts.xml for an
+example. Likewise you have to add a /etc/caldavd/sudoers.plist. Both files have
+to be present, otherwise the calendarserver will not work.
+
+By default calendarserver listens on localhost only so the URI to your caldav
+calendar will typically look like:
+
+ http://localhost:8008/calendars/users/<user>/calendar/
+
+where <user> is the username of a calendarserver user as specified in
+accounts.xml. And for groups defined in accounts.xml it's:
+
+ http://localhost:8008/calendars/groups/<group>/calendar/
+
+
+Loadbalancing
+=============
+In order to enable laod balancing onto different processors/cores you need to
+install python-pydirecotor and set the ProcessCount in
+/etc/caldavd/caldavd.plist accordingly.
+
+
+Enabling SPNEGO/Kerberos
+========================
+
+To make SPNEGO/Kerberos authentication work you have to add service principals
+for HTTP/caldavd.example.com@EXAMPLE.COM and
+http/caldavd.example.com@EXAMPLE.COM to your servers keytab /etc/krb5.keytab
+(replace caldavd.example.com by the fqdn of your caldav server and EXAMPLE.COM
+by your Kerberos realm).
+The uppercase http variant is used by most clients like iceowl and icedove with
+iceowl-extension while the lowercase version is used by iCal. The keytab must
+be readable for user caldavd which can be achieved by:
+
+chgrp caldavd /etc/krb5.keytab
+chmod 0640 /etc/krb5.keytab
+
+Now you have to specify the name of a ServicePrincipal in
+/etc/caldavd/caldavd.plist:
+
+ <!-- Kerberos/SPNEGO -->
+ <key>Kerberos</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>ServicePrincipal</key>
+ <string>http/caldavd.example.com@EXAMPLE.COM</string>
+ </dict>
+
+Note: if you use iceowl/iceowl-extension the
+network.negotiate-auth.trusted-uris in iceowl/icedove must match on your
+calendarservers uri otherwise SPNEGO will not work. A good choice is
+"https://".
+
+
+Nameservice Switch Backend
+==========================
+If you don't want to manage accounts in a separate XML file you can use the
+names service switch backend. Details on how to set this up can be found at:
+
+http://honk.sigxcpu.org/con/Apple_Calendarser_with_Name_Service_Switch_directory_backend.html
+
+Note that in order to function properly "getent passwd" must list all users
+that should be able to access the calendarserver. This might not be the case if
+you hit the search limit of your LDAP server.
+
+Also note that the username and groupname (without the prefix) shouldn't be the same else
+calendarserver might get confused.
+
+PAM authentication is supported. The pam service name is "caldav". Basic Authentication should
+be enabled and Digest Authentication should be disabled for PAM authentication to work. It is
+suggested to use PAM authentication in conjunction with SSL (https) so that the password is not
+sent in plain text. For PAM authentication against local unix passwords, the user "caldavd"
+should be added to the "shadow" group.
+
+ -- Guido Guenther <agx@sigxcpu.org> Thu, 30 Apr 2008 16:17:56 +0100
+
+
+LDAP Directory Backend
+======================
+Details of LDAP Directory backend can be found at:
+
+http://trac.calendarserver.org/ticket/260
+
+Possible authentication methods are "LDAP" (default) and "PAM". The pam service name is "caldav".
+For PAM/LDAP authentication, Basic Authentication should be enabled and Digest Authentication should
+be disabled (as shown below). It is suggested to use PAM/LDAP authentication in conjunction with
+SSL (https) so that the password is not sent in plain text. For PAM authentication against local
+unix passwords, the user "caldavd" should be added to the "shadow" group.
+
+ <!-- Clear text; best avoided -->
+ <key>Basic</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ </dict>
+
+ <!-- Digest challenge/response -->
+ <key>Digest</key>
+ <dict>
+ <key>Enabled</key>
+ <false/>
+ <key>Algorithm</key>
+ <string>md5</string>
+ <key>Qop</key>
+ <string></string>
+ </dict>
+
+For ldap anonymous binding, the dn and password entries should be present in the configuration file
+but their values should be empty.
+
+ -- Rahul Amaram <amaramrahul@users.sourceforge.net> Tue, 24 Aug 2010 22:05:00 +0530
--- calendarserver-2.4.dfsg.orig/debian/NEWS
+++ calendarserver-2.4.dfsg/debian/NEWS
@@ -0,0 +1,7 @@
+calendarserver (2.4.dfsg-3) unstable; urgency=low
+
+ * Group calendaring has been disabled in calendarserver 2.x. For more information
+ on this, see http://trac.calendarserver.org/ticket/330.
+
+ -- Rahul Amaram <amaramrahul@users.sourceforge.net> Fri, 17 Dec 2010 00:00:00 +0530
+
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.init.d
+++ calendarserver-2.4.dfsg/debian/calendarserver.init.d
@@ -0,0 +1,97 @@
+#! /bin/sh
+#
+# calendarserver startup script
+#
+### BEGIN INIT INFO
+# Provides: caldavserver
+# Required-Start: $remote_fs $network
+# Required-Stop: $remote_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: CalDAV Calendarserver
+### END INIT INFO
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/bin/caldavd
+NAME=caldavd
+DESC=calendarserver
+RUNDIR=/var/run/caldavd/
+SPOOLDIR=/var/spool/caldavd/
+
+test -x $DAEMON || exit 0
+
+# Include calendarserver defaults if available
+if [ -f /etc/default/calendarserver ] ; then
+ . /etc/default/calendarserver
+fi
+
+. /lib/lsb/init-functions
+
+set -e
+
+check_start_daemon() {
+ if [ ! "$start_calendarserver" = "yes" ]; then
+ log_warning_msg "Not starting calendarserver, disabled via /etc/default/calendarserver"
+ return 1
+ else
+ return 0
+ fi
+}
+
+case "$1" in
+ start)
+ if check_start_daemon; then
+ log_daemon_msg "Starting $DESC" "$NAME"
+ mkdir -p $RUNDIR
+ chown --reference=$SPOOLDIR $RUNDIR
+ if start-stop-daemon --start --quiet --pidfile $RUNDIR$NAME.pid \
+ --exec $DAEMON -- $DAEMON_OPTS 2>/dev/null; then
+ log_end_msg 0
+ RET=0
+ else
+ log_end_msg 1
+ RET=1
+ fi
+ fi
+ ;;
+ stop)
+ log_daemon_msg "Stopping $DESC" "$NAME"
+ if start-stop-daemon --oknodo --stop --quiet --pidfile $RUNDIR$NAME.pid \
+ --exec /usr/bin/python; then
+ log_end_msg 0
+ RET=0
+ else
+ log_end_msg 1
+ RET=1
+ fi
+ ;;
+ restart|force-reload)
+ #
+ # If the "reload" option is implemented, move the "force-reload"
+ # option to the "reload" entry above. If not, "force-reload" is
+ # just the same as "restart".
+ #
+ if check_start_daemon; then
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ start-stop-daemon --stop --quiet --oknodo --pidfile \
+ $RUNDIR$NAME.pid --exec /usr/bin/python
+ sleep 1
+ if start-stop-daemon --start --quiet --pidfile \
+ $RUNDIR$NAME.pid --exec $DAEMON -- $DAEMON_OPTS 2>/dev/null; then
+ log_end_msg 0
+ RET=0
+ else
+ log_end_msg 1
+ RET=1
+ fi
+ fi
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit $RET
--- calendarserver-2.4.dfsg.orig/debian/compat
+++ calendarserver-2.4.dfsg/debian/compat
@@ -0,0 +1 @@
+5
--- calendarserver-2.4.dfsg.orig/debian/pyversions
+++ calendarserver-2.4.dfsg/debian/pyversions
@@ -0,0 +1 @@
+2.5-
--- calendarserver-2.4.dfsg.orig/debian/rules
+++ calendarserver-2.4.dfsg/debian/rules
@@ -0,0 +1,30 @@
+#!/usr/bin/make -f
+
+DEB_PYTHON_SYSTEM = pycentral
+
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/python-distutils.mk
+include /usr/share/cdbs/1/rules/patchsys-quilt.mk
+
+DEB_DESTDIR = $(CURDIR)/debian/calendarserver
+DEB_DH_INSTALLPAM_ARGS := --name=caldav
+
+calendarserver-doc-api-stamp:
+ #cd doc/Developer && pydoctor -c twistedcaldav.cfg --resolve-aliases --make-html
+ touch calendarserver-doc-api-stamp
+
+build/calendarserver-doc-api:: calendarserver-doc-api-stamp
+
+install/calendarserver::
+ rm -rf $(DEB_DESTDIR)
+ set -e; for buildver in $(cdbs_python_build_versions); do \
+ cd $(CURDIR) && cd $(DEB_SRCDIR) && python$$buildver setup.py install --root=$(DEB_DESTDIR) --install-purelib=/usr/lib/twisted-calendarserver/lib/python$$buildver/site-packages/ --install-platlib=/usr/lib/twisted-calendarserver/lib/python$$buildver/site-packages/ $(DEB_PYTHON_INSTALL_ARGS_ALL); \
+ done
+
+binary-fixup/calendarserver::
+ install -D -m 0755 -p -o root -g root $(CURDIR)/debian/upgrade-nss-data-directories.py $(DEB_DESTDIR)/usr/share/doc/calendarserver/scripts/upgrade-nss-data-directories.py
+
+clean::
+ -rm twistedcaldav/version.py
+ -rm -rf doc/Developer/apidocs calendarserver-doc-api-stamp
+
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.TODO
+++ calendarserver-2.4.dfsg/debian/calendarserver.TODO
@@ -0,0 +1,2 @@
+
+
--- calendarserver-2.4.dfsg.orig/debian/calendarserver.caldav.pam
+++ calendarserver-2.4.dfsg/debian/calendarserver.caldav.pam
@@ -0,0 +1,6 @@
+#%PAM-1.0
+
+@include common-auth
+@include common-account
+@include common-session
+
--- calendarserver-2.4.dfsg.orig/debian/README.source
+++ calendarserver-2.4.dfsg/debian/README.source
@@ -0,0 +1,57 @@
+This package uses quilt to manage all modifications to the upstream
+source. Changes are stored in the source package as diffs in
+debian/patches and applied during the build.
+
+To configure quilt to use debian/patches instead of patches, you want
+either to export QUILT_PATCHES=debian/patches in your environment
+or use this snippet in your ~/.quiltrc:
+
+ for where in ./ ../ ../../ ../../../ ../../../../ ../../../../../; do
+ if [ -e ${where}debian/rules -a -d ${where}debian/patches ]; then
+ export QUILT_PATCHES=debian/patches
+ fi
+ done
+
+To get the fully patched source after unpacking the source package, cd to
+the root level of the source package and run:
+
+ quilt push -a
+
+The last patch listed in debian/patches/series will become the current
+patch.
+
+To add a new set of changes, first run quilt push -a, and then run:
+
+ quilt new <patch>
+
+where <patch> is a descriptive name for the patch, used as the filename in
+debian/patches. Then, for every file that will be modified by this patch,
+run:
+
+ quilt add <file>
+
+before editing those files. You must tell quilt with quilt add what files
+will be part of the patch before making changes or quilt will not work
+properly. After editing the files, run:
+
+ quilt refresh
+
+to save the results as a patch.
+
+Alternately, if you already have an external patch and you just want to
+add it to the build system, run quilt push -a and then:
+
+ quilt import -P <patch> /path/to/patch
+ quilt push -a
+
+(add -p 0 to quilt import if needed). <patch> as above is the filename to
+use in debian/patches. The last quilt push -a will apply the patch to
+make sure it works properly.
+
+To remove an existing patch from the list of patches that will be applied,
+run:
+
+ quilt delete <patch>
+
+You may need to run quilt pop -a to unapply patches first before running
+this command.
--- calendarserver-2.4.dfsg.orig/debian/upgrade-nss-data-directories.py
+++ calendarserver-2.4.dfsg/debian/upgrade-nss-data-directories.py
@@ -0,0 +1,159 @@
+#!/usr/bin/python
+
+import os
+import shutil
+import pwd
+import grp
+import plistlib
+import copy
+import StringIO
+from uuid import UUID, uuid5
+
+
+def main():
+
+ # Init params
+ defaultConfigFile = "/etc/caldavd/caldavd.plist.upgrade.nss.tmp"
+ defaultConfig = {
+ "DirectoryService": {
+ "type": "twistedcaldav.directory.xmlfile.XMLDirectoryService",
+ },
+ "DocumentRoot": "/Library/CalendarServer/Documents",
+ }
+ serviceDefaultParams = {
+ "twistedcaldav.directory.nss.NssDirectoryService": {
+ "realmName": "Test Realm",
+ "groupPrefix": "caldavd-",
+ "firstValidUid": 1000,
+ "lastValidUid": 65533,
+ "firstValidGid": 1000,
+ "lastValidGid": 65533,
+ }
+ }
+ recordType_users = "users"
+ recordType_groups = "groups"
+ nssServiceBaseGUID = "8EFFFAF1-5221-4813-B971-58506B963573"
+
+ # Function to generate uuid from name
+ def uuidFromName(namespace, name):
+ """
+ Generate a version 5 (SHA-1) UUID from a namespace UUID and a name.
+ See http://www.ietf.org/rfc/rfc4122.txt, section 4.3.
+ @param namespace: a UUID denoting the namespace of the generated UUID.
+ @param name: a byte string to generate the UUID from.
+ """
+ # We don't want Unicode here; convert to UTF-8
+ if type(name) is unicode:
+ name = name.encode("utf-8")
+
+ return str(uuid5(UUID(namespace), name))
+
+ # Function to update config
+ def _update_config(configElement, defaultConfigElement):
+ for key, value in defaultConfigElement.iteritems():
+ if key not in configElement:
+ configElement[key] = copy.deepcopy(value)
+ elif isinstance(configElement[key], dict) and \
+ isinstance(value, dict):
+ _update_config(configElement[key], value)
+
+
+ # Function to move individual user/group directory
+ def _move_directory(nssServiceGUID, documentRoot, recordType, shortName):
+ guid = uuidFromName(nssServiceGUID, "%s:%s" % (recordType, shortName))
+ olddir = os.path.join(documentRoot, "calendars", "__uids__", shortName)
+ newdir = os.path.join(documentRoot, "calendars", "__uids__", guid)
+ print "Moving directory for %s '%s' (GUID: %s) ..." % \
+ (recordType[:-1], shortName, guid)
+ if (not os.path.isdir(olddir)):
+ print "Old data directory '%s' does not exist. Nothing to move." % \
+ (olddir)
+ return
+ if (os.path.isdir(newdir)):
+ print "New data directory '%s' already exists. Aborting move." % \
+ (newdir)
+ return
+ shutil.move(olddir, newdir)
+ print "Data directory successfully moved from '%s' to '%s'." % \
+ (olddir, newdir)
+
+ # Begin
+ print ("Moving caldavd directories of NSS users and groups as the "
+ "directory names in calendarserver 2.x are based on UUID "
+ "rather than username/groupname as in calendarserver 1.x "
+ "(See Debian Bug#610124 for more info) ...\n")
+
+ # Check if config file exists.
+ if not os.path.isfile(defaultConfigFile):
+ print "Caldavd config file %s does not exist. Exiting." % \
+ (defaultConfigFile)
+ return
+
+ # Load configuration file
+ configPlist = plistlib.readPlist(defaultConfigFile)
+
+ # Update default configuration
+ s = StringIO.StringIO()
+ plistlib.writePlist(defaultConfig, s)
+ defaultConfigPlist = plistlib.readPlistFromString(s.getvalue())
+ _update_config(configPlist, defaultConfigPlist)
+
+ # If NSS is not the default directory service, exit
+ if configPlist.DirectoryService.type != \
+ "twistedcaldav.directory.nss.NssDirectoryService":
+ print "Directory service NssDirectoryService is not configured in " \
+ "%s. Exiting." % (defaultConfigFile)
+ return
+
+ # Update with service default params
+ s = StringIO.StringIO()
+ plistlib.writePlist(
+ {"params": serviceDefaultParams[
+ "twistedcaldav.directory.nss.NssDirectoryService"]},
+ s
+ )
+ nssServiceDefaultParamsPlist = plistlib.readPlistFromString(s.getvalue())
+ _update_config(configPlist.DirectoryService, nssServiceDefaultParamsPlist)
+
+ # Generate serviceGUID
+ nssServiceGUID = uuidFromName(
+ nssServiceBaseGUID,
+ configPlist.DirectoryService.params.realmName
+ )
+ print "NSS Directory Service GUID - %s\n" % (nssServiceGUID)
+
+ # Move users' data directories
+ print "Moving data directories of users ..."
+ users = pwd.getpwall()
+ for user in users:
+ if user[2] >= configPlist.DirectoryService.params.firstValidUid and \
+ user[2] <= configPlist.DirectoryService.params.lastValidUid:
+ _move_directory(
+ nssServiceGUID,
+ configPlist.DocumentRoot,
+ recordType_users,
+ user[0]
+ )
+ print
+
+ # Move groups' data directories
+ print "Moving data directories of groups ..."
+ groups = grp.getgrall()
+ for group in groups:
+ if group[2] >= configPlist.DirectoryService.params.firstValidGid and \
+ group[2] <= configPlist.DirectoryService.params.lastValidGid and \
+ group[0].startswith(configPlist.DirectoryService.params.groupPrefix):
+ _move_directory(
+ nssServiceGUID,
+ configPlist.DocumentRoot,
+ recordType_groups,
+ group[0].replace(
+ configPlist.DirectoryService.params.groupPrefix,'',1
+ )
+ )
+ print
+
+
+if __name__ == "__main__":
+ main()
+
--- calendarserver-2.4.dfsg.orig/debian/patches/secure-python-path.patch
+++ calendarserver-2.4.dfsg/debian/patches/secure-python-path.patch
@@ -0,0 +1,64 @@
+Removes setting PYTHONPATH in an insecure way, not needed on Debian.
+Removes "rewritting" rules from setup.py that did that.
+Index: b/setup.py
+===================================================================
+--- a/setup.py 2010-12-09 19:21:45.000000000 -0500
++++ b/setup.py 2010-12-09 19:22:25.000000000 -0500
+@@ -125,43 +125,3 @@
+ if root:
+ install_lib = install_lib[len(root):]
+
+- for script in dist.scripts:
+- scriptPath = os.path.join(install_scripts, os.path.basename(script))
+-
+- print "rewriting %s" % (scriptPath,)
+-
+- script = []
+-
+- fileType = None
+-
+- for line in file(scriptPath, "r"):
+- if not fileType:
+- if line.startswith("#!"):
+- if "python" in line.lower():
+- fileType = "python"
+- elif "sh" in line.lower():
+- fileType = "sh"
+-
+- line = line.rstrip("\n")
+- if fileType == "sh":
+- if line == "#PYTHONPATH":
+- script.append('PYTHONPATH="%s:$PYTHONPATH"' % (install_lib,))
+- elif line == "#PATH":
+- script.append('PATH="%s:$PATH"' % (os.path.join(base, "bin"),))
+- else:
+- script.append(line)
+-
+- elif fileType == "python":
+- if line == "#PYTHONPATH":
+- script.append('PYTHONPATH="%s"' % (install_lib,))
+- elif line == "#PATH":
+- script.append('PATH="%s"' % (os.path.join(base, "bin"),))
+- else:
+- script.append(line)
+-
+- else:
+- script.append(line)
+-
+- newScript = open(scriptPath, "w")
+- newScript.write("\n".join(script))
+- newScript.close()
+Index: b/bin/caldavd
+===================================================================
+--- a/bin/caldavd 2010-12-09 19:21:51.000000000 -0500
++++ b/bin/caldavd 2010-12-09 19:22:41.000000000 -0500
+@@ -16,9 +16,6 @@
+ # limitations under the License.
+ ##
+
+-#PATH
+-#PYTHONPATH
+-
+ daemonize="";
+ username="";
+ groupname="";
--- calendarserver-2.4.dfsg.orig/debian/patches/paths.diff
+++ calendarserver-2.4.dfsg/debian/patches/paths.diff
@@ -0,0 +1,175 @@
+diff -Naur calendarserver-2.4.dfsg.orig/bin/caldavd calendarserver-2.4.dfsg/bin/caldavd
+--- calendarserver-2.4.dfsg.orig/bin/caldavd 2008-10-29 05:06:36.000000000 +0530
++++ calendarserver-2.4.dfsg/bin/caldavd 2010-08-24 21:59:30.000000000 +0530
+@@ -23,7 +23,7 @@
+ username="";
+ groupname="";
+ configfile="";
+-twistdpath="$(type -p twistd)";
++twistdpath="/usr/lib/twisted-calendarserver/bin/twistd";
+ plugin_name="caldav";
+ service_type="";
+ profile="";
+@@ -116,7 +116,7 @@
+
+ if [ $# != 0 ]; then usage "Unrecognized arguments:" "$@"; fi;
+
+-export PYTHONPATH
++export PYTHONPATH=`${python} -c 'import sys; print "/usr/lib/twisted-calendarserver/lib/python%s/site-packages" % (sys.version[:3])'`
+
+ echo exec "${python}" "${twistdpath}" "${twistd_reactor}" ${daemonize} ${username} ${groupname} "${plugin_name}" ${configfile} ${service_type} ${profile} "${child_reactor}";
+
+diff -Naur calendarserver-2.4.dfsg.orig/bin/calendarserver_export calendarserver-2.4.dfsg/bin/calendarserver_export
+--- calendarserver-2.4.dfsg.orig/bin/calendarserver_export 2009-05-08 02:42:08.000000000 +0530
++++ calendarserver-2.4.dfsg/bin/calendarserver_export 2010-08-24 21:59:30.000000000 +0530
+@@ -17,7 +17,7 @@
+
+ import sys
+
+-#PYTHONPATH
++PYTHONPATH = "/usr/lib/twisted-calendarserver/lib/python%s/site-packages" % (sys.version[:3])
+
+ if __name__ == "__main__":
+ if "PYTHONPATH" in globals():
+diff -Naur calendarserver-2.4.dfsg.orig/bin/calendarserver_manage_principals calendarserver-2.4.dfsg/bin/calendarserver_manage_principals
+--- calendarserver-2.4.dfsg.orig/bin/calendarserver_manage_principals 2009-05-08 02:59:14.000000000 +0530
++++ calendarserver-2.4.dfsg/bin/calendarserver_manage_principals 2010-08-24 21:59:30.000000000 +0530
+@@ -19,7 +19,7 @@
+ from __future__ import with_statement
+ import sys
+
+-#PYTHONPATH
++PYTHONPATH = "/usr/lib/twisted-calendarserver/lib/python%s/site-packages" % (sys.version[:3])
+
+ if __name__ == "__main__":
+ if "PYTHONPATH" in globals():
+diff -Naur calendarserver-2.4.dfsg.orig/bin/calendarserver_warmup calendarserver-2.4.dfsg/bin/calendarserver_warmup
+--- calendarserver-2.4.dfsg.orig/bin/calendarserver_warmup 2009-05-09 04:04:49.000000000 +0530
++++ calendarserver-2.4.dfsg/bin/calendarserver_warmup 2010-08-24 21:59:30.000000000 +0530
+@@ -17,7 +17,7 @@
+
+ import sys
+
+-#PYTHONPATH
++PYTHONPATH = "/usr/lib/twisted-calendarserver/lib/python%s/site-packages" % (sys.version[:3])
+
+ if __name__ == "__main__":
+ if "PYTHONPATH" in globals():
+diff -Naur calendarserver-2.4.dfsg.orig/conf/caldavd.plist calendarserver-2.4.dfsg/conf/caldavd.plist
+--- calendarserver-2.4.dfsg.orig/conf/caldavd.plist 2010-08-24 21:58:56.000000000 +0530
++++ calendarserver-2.4.dfsg/conf/caldavd.plist 2010-08-24 21:59:30.000000000 +0530
+@@ -36,13 +36,13 @@
+
+ <!-- HTTP port [0 = disable HTTP] -->
+ <key>HTTPPort</key>
+- <integer>80</integer>
++ <integer>8008</integer>
+
+ <!-- SSL port [0 = disable HTTPS] -->
+ <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+ <!--
+ <key>SSLPort</key>
+- <integer>443</integer>
++ <integer>8443</integer>
+ -->
+
+ <!-- Redirect non-SSL ports to an SSL port (if configured for SSL) -->
+@@ -78,11 +78,11 @@
+
+ <!-- Data root -->
+ <key>DataRoot</key>
+- <string>/Library/CalendarServer/Data/</string>
++ <string>/var/lib/caldavd/</string>
+
+ <!-- Document root -->
+ <key>DocumentRoot</key>
+- <string>/Library/CalendarServer/Documents/</string>
++ <string>/var/spool/caldavd/</string>
+
+ <!-- Child aliases -->
+ <key>Aliases</key>
+@@ -391,7 +391,7 @@
+
+ <!-- Global server stats -->
+ <key>GlobalStatsSocket</key>
+- <string>/var/run/caldavd-stats.sock</string>
++ <string>/var/run/caldavd/caldavd-stats.sock</string>
+
+ <!-- Server statistics file -->
+ <key>ServerStatsFile</key>
+@@ -399,7 +399,7 @@
+
+ <!-- Server process ID file -->
+ <key>PIDFile</key>
+- <string>/var/run/caldavd.pid</string>
++ <string>/var/run/caldavd/caldavd.pid</string>
+
+
+ <!--
+@@ -420,10 +420,10 @@
+ -->
+
+ <key>UserName</key>
+- <string>daemon</string>
++ <string>caldavd</string>
+
+ <key>GroupName</key>
+- <string>daemon</string>
++ <string>caldavd</string>
+
+ <key>ProcessType</key>
+ <string>Combined</string>
+@@ -431,7 +431,7 @@
+ <key>MultiProcess</key>
+ <dict>
+ <key>ProcessCount</key>
+- <integer>0</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
++ <integer>1</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
+ </dict>
+
+
+@@ -588,6 +588,32 @@
+ <key>EnableWebAdmin</key>
+ <true/>
+
++ <!-- Twisted -->
++ <key>Twisted</key>
++ <dict>
++ <key>twistd</key>
++ <string>/usr/lib/twisted-calendarserver/bin/twistd</string>
++ </dict>
++
++ <!-- Python Director -->
++ <key>PythonDirector</key>
++ <dict>
++ <key>pydir</key>
++ <string>/usr/share/pydirector/pydir.py</string>
++ <key>ControlSocket</key>
++ <string>/var/run/caldavd/caldavd-pydir.sock</string>
++ </dict>
++
++ <!-- Control Socket -->
++ <key>ControlSocket</key>
++ <string>/var/run/caldavd/caldavd.sock</string>
++
++ <!-- Memcached -->
++ <key>Memcached</key>
++ <dict>
++ <key>ServerEnabled</key>
++ <false/>
++ </dict>
+
+ </dict>
+ </plist>
+diff -Naur calendarserver-2.4.dfsg.orig/setup.py calendarserver-2.4.dfsg/setup.py
+--- calendarserver-2.4.dfsg.orig/setup.py 2009-05-08 02:42:08.000000000 +0530
++++ calendarserver-2.4.dfsg/setup.py 2010-08-24 21:59:30.000000000 +0530
+@@ -110,7 +110,7 @@
+ "bin/calendarserver_export",
+ "bin/calendarserver_manage_principals"
+ ],
+- data_files = [ ("caldavd", ["conf/caldavd.plist"]) ],
++ data_files = [ ("/etc/caldavd", ["conf/caldavd.plist", "conf/sudoers.plist", "conf/auth/accounts.xml"]) ],
+ ext_modules = extensions,
+ py_modules = ["kqreactor", "memcacheclient"],
+ )
--- calendarserver-2.4.dfsg.orig/debian/patches/ldapdirectory.patch
+++ calendarserver-2.4.dfsg/debian/patches/ldapdirectory.patch
@@ -0,0 +1,634 @@
+Index: calendarserver-2.4.dfsg/conf/caldavd.plist
+===================================================================
+--- calendarserver-2.4.dfsg.orig/conf/caldavd.plist 2010-08-24 21:22:46.000000000 +0530
++++ calendarserver-2.4.dfsg/conf/caldavd.plist 2010-08-24 21:49:54.000000000 +0530
+@@ -191,6 +191,100 @@
+ </dict>
+ -->
+
++ <!-- OpenLDAP Directory Service -->
++ <!--
++ <key>DirectoryService</key>
++ <dict>
++ <key>type</key>
++ <string>twistedcaldav.directory.ldapdirectory.LdapDirectoryService</string>
++
++ <key>params</key>
++ <dict>
++ <key>cacheTimeout</key>
++ <integer>30</integer>
++ <key>realmName</key>
++ <string>Test Realm</string>
++ <key>uri</key>
++ <string>ldap://localhost:389/</string>
++ <key>tls</key>
++ <false/>
++ <key>tlsCACertFile</key>
++ <string></string>
++ <key>tlsCACertDir</key>
++ <string></string>
++ <key>tlsRequireCert</key>
++ <string>demand</string>
++ <key>credentials</key>
++ <dict>
++ <key>dn</key>
++ <string></string>
++ <key>password</key>
++ <string></string>
++ </dict>
++ <key>authMethod</key>
++ <string>PAM</string>
++ <key>rdnSchema</key>
++ <dict>
++ <key>base</key>
++ <string>dc=example,dc=com</string>
++ <key>guidAttr</key>
++ <string>entryUUID</string>
++ <key>users</key>
++ <dict>
++ <key>rdn</key>
++ <string>ou=People</string>
++ <key>attr</key>
++ <string>uid</string>
++ <key>emailSuffix</key>
++ <string></string>
++ <key>filter</key>
++ <string>(objectClass=inetOrgPerson)</string>
++ </dict>
++ <key>groups</key>
++ <dict>
++ <key>rdn</key>
++ <string>ou=Group</string>
++ <key>attr</key>
++ <string>cn</string>
++ <key>emailSuffix</key>
++ <string></string>
++ <key>filter</key>
++ <string></string>
++ </dict>
++ <key>locations</key>
++ <dict>
++ <key>rdn</key>
++ <string>ou=Locations</string>
++ <key>attr</key>
++ <string>cn</string>
++ <key>emailSuffix</key>
++ <string></string>
++ <key>filter</key>
++ <string></string>
++ </dict>
++ <key>resources</key>
++ <dict>
++ <key>rdn</key>
++ <string>ou=Resources</string>
++ <key>attr</key>
++ <string>cn</string>
++ <key>emailSuffix</key>
++ <string></string>
++ <key>filter</key>
++ <string></string>
++ </dict>
++ </dict>
++ <key>groupSchema</key>
++ <dict>
++ <key>membersAttr</key>
++ <string>member</string>
++ <key>memberIdAttr</key>
++ <string></string>
++ </dict>
++ </dict>
++ </dict>
++ -->
++
+ <!--
+ Special principals
+
+Index: calendarserver-2.4.dfsg/twistedcaldav/directory/ldapdirectory.py
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ calendarserver-2.4.dfsg/twistedcaldav/directory/ldapdirectory.py 2010-08-24 21:49:29.000000000 +0530
+@@ -0,0 +1,524 @@
++##
++# Copyright (c) 2008-2009 Aymeric Augustin. All rights reserved.
++#
++# Licensed under the Apache License, Version 2.0 (the "License");
++# you may not use this file except in compliance with the License.
++# You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++##
++
++"""
++LDAP directory service implementation.
++
++The following attributes from standard schemas are used:
++* Core (RFC 4519):
++ . cn | commonName
++ . givenName
++ . member (if not using NIS groups)
++ . ou
++ . sn | surname
++ . uid | userid (if using NIS groups)
++* COSINE (RFC 4524):
++ . mail
++* InetOrgPerson (RFC 2798):
++ . displayName (if cn is unavailable)
++* NIS (RFC):
++ . gecos (if cn is unavailable)
++ . memberUid (if using NIS groups)
++"""
++
++__all__ = [
++ "LdapDirectoryService",
++]
++
++import ldap
++import PAM
++
++from twisted.cred.credentials import UsernamePassword
++from twisted.web2.auth.digest import DigestedCredentials
++from twistedcaldav.directory.cachingdirectory import CachingDirectoryService,\
++ CachingDirectoryRecord
++
++
++class LdapDirectoryService(CachingDirectoryService):
++ """
++ LDAP based implementation of L{IDirectoryService}.
++ """
++ baseGUID = "5A871574-0C86-44EE-B11B-B9440C3DC4DD"
++
++ def __repr__(self):
++ return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.uri)
++
++ def __init__(self, params, dosetup=True):
++ """
++ @param params: a dictionary containing the following keys:
++ cacheTimeout, realmName, uri, tls, tlsCACertFile, tlsCACertDir,
++ tlsRequireCert, crendentials, rdnSchema, groupSchema
++ @param dosetup: if C{True} then the directory records are initialized,
++ if C{False} they are not.
++ This should only be set to C{False} when doing unit tests.
++ """
++
++ defaults = {
++ "cacheTimeout": 30,
++ "realmName": "Test Realm",
++ "uri": "ldap://localhost/",
++ "tls": False,
++ "tlsCACertFile": None,
++ "tlsCACertDir": None,
++ "tlsRequireCert": None, # never, allow, try, demand/hard
++ "credentials": {
++ "dn": None,
++ "password": None,
++ },
++ "authMethod": "LDAP",
++ "rdnSchema": {
++ "base": "dc=example,dc=com",
++ "guidAttr": None,
++ "users": {
++ "rdn": "ou=People",
++ "attr": "uid",
++ "emailSuffix": None,
++ "filter": None,
++ },
++ "groups": {
++ "rdn": "ou=Group",
++ "attr": "cn",
++ "emailSuffix": None,
++ "filter": None,
++ },
++ "locations": {
++ "rdn": "ou=Locations",
++ "attr": "cn",
++ "emailSuffix": None,
++ "filter": None,
++ },
++ "resources": {
++ "rdn": "ou=Resources",
++ "attr": "cn",
++ "emailSuffix": None,
++ "filter": None,
++ },
++ },
++ "groupSchema": {
++ "membersAttr": "member",
++ "memberIdAttr": None,
++ },
++ }
++ ignored = None
++ params = self.getParams(params, defaults, ignored)
++
++ super(LdapDirectoryService, self).__init__(params['cacheTimeout'])
++
++ self.realmName = params['realmName']
++ self.uri = params['uri']
++ self.tls = params['tls']
++ self.tlsCACertFile = params['tlsCACertFile']
++ self.tlsCACertDir = params['tlsCACertDir']
++ self.tlsRequireCert = params['tlsRequireCert']
++ self.credentials = params['credentials']
++ self.authMethod = params['authMethod']
++ self.rdnSchema = params['rdnSchema']
++ self.groupSchema = params['groupSchema']
++
++ # Certain attributes (such as entryUUID) may be hidden and not
++ # returned by default when queried for all attributes. Therefore it is
++ # necessary to explicitly pass all the possible attributes list
++ # for ldap searches
++ attrSet = set(['mail', 'uid', 'userid', 'cn', 'commonName',
++ 'displayName', 'gecos', 'givenName', 'sn', 'surname'])
++ if self.rdnSchema['guidAttr']:
++ attrSet.add(self.rdnSchema['guidAttr'])
++ for recordType in self.recordTypes():
++ if self.rdnSchema[recordType]['attr']:
++ attrSet.add(self.rdnSchema[recordType]['attr'])
++ if self.groupSchema['membersAttr']:
++ attrSet.add(self.groupSchema['membersAttr'])
++ if self.groupSchema['memberIdAttr']:
++ attrSet.add(self.groupSchema['memberIdAttr'])
++ self.attrList = list(attrSet)
++
++ # Create LDAP connection
++ self.log_info("Calling ldap.ldapobject.ReconnectLDAPObject(%s)." % repr(self.uri), system="LdapDirectoryService")
++ self.ldap = ldap.ldapobject.ReconnectLDAPObject(self.uri)
++
++ # Open LDAP Connection
++ if self.tlsCACertFile:
++ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.tlsCACertFile)
++ if self.tlsCACertDir:
++ ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, self.tlsCACertDir)
++
++ if self.tlsRequireCert == "never":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_NEVER)
++ elif self.tlsRequireCert == "allow":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_ALLOW)
++ elif self.tlsRequireCert == "try":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_TRY)
++ elif self.tlsRequireCert == "demand":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND)
++ elif self.tlsRequireCert == "hard":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_HARD)
++
++ if self.tls:
++ self.ldap.start_tls_s()
++
++ if self.credentials.get('dn', ''):
++ try:
++ self.log_info("Calling LDAPObject.simple_bind_s(%s, [password])." % repr(self.credentials.get('dn')), system="LdapDirectoryService")
++ self.ldap.simple_bind_s(self.credentials.get('dn'), self.credentials.get('password'))
++ except ldap.INVALID_CREDENTIALS:
++ self.log_error("Unable to bind to LDAP server %s: check credentials." % self.uri, system="LdapDirectoryService")
++ raise
++
++ def __del__(self):
++ try: self.ldap.unbind_s()
++ except: pass
++
++ def recordTypes(self):
++ return (
++ self.recordType_users,
++ self.recordType_groups,
++ self.recordType_locations,
++ self.recordType_resources,
++ )
++
++ # Get the first value for one or several attributes
++ # Useful when attributes have aliases (e.g. sn vs. surname)
++ def _getUniqueLdapAttribute(self, attrs, *keys):
++ for key in keys:
++ values = attrs.get(key)
++ if values is not None:
++ return values[0]
++ return None
++
++ # Get all values for one or several attributes
++ def _getMultipleLdapAttributes(self, attrs, *keys):
++ results = []
++ for key in keys:
++ values = attrs.get(key)
++ if values is not None:
++ results += values
++ return set(results)
++
++ # Convert the attrs returned by a LDAP search into a LdapDirectoryRecord object
++ # Mappings are hardcoded below but the most standard LDAP schemas were used
++ # to define them
++ def _ldapResultToRecord(self, dn, attrs, recordType):
++ guid = None
++ shortNames = ()
++ authIDs = set()
++ fullName = None
++ firstName = None
++ lastName = None
++ emailAddresses = set()
++ calendarUserAddresses = set()
++ enabledForCalendaring = None
++ uid = None
++
++ # First check for and add guid
++ guidAttr = self.rdnSchema['guidAttr']
++ if guidAttr:
++ guid = self._getUniqueLdapAttribute(attrs, guidAttr)
++
++ # Find or build email
++ emailAddresses = set(map(lambda v: 'mailto:' + v, self._getMultipleLdapAttributes(attrs, 'mail')))
++ emailSuffix = self.rdnSchema[recordType]['emailSuffix']
++ if len(emailAddresses) == 0 and emailSuffix is not None:
++ emailPrefix = self._getUniqueLdapAttribute(attrs, self.rdnSchema[recordType]['attr'])
++ emailAddresses.add('mailto:' + emailPrefix + emailSuffix)
++
++ # LDAP attribute -> principal matchings
++ if recordType == self.recordType_users:
++ shortNames = (self._getUniqueLdapAttribute(attrs, 'uid', 'userid'),)
++ fullName = self._getUniqueLdapAttribute(attrs, 'cn', 'commonName', 'displayName', 'gecos')
++ firstName = self._getUniqueLdapAttribute(attrs, 'givenName')
++ lastName = self._getUniqueLdapAttribute(attrs, 'sn', 'surname')
++ calendarUserAddresses = emailAddresses
++ enabledForCalendaring = True
++ elif recordType == self.recordType_groups:
++ shortNames = (self._getUniqueLdapAttribute(attrs, 'cn'),)
++ fullName = self._getUniqueLdapAttribute(attrs, 'cn')
++ enabledForCalendaring = False
++ elif recordType in (self.recordType_resources, self.recordType_locations):
++ shortNames = (self._getUniqueLdapAttribute(attrs, 'cn'),)
++ fullName = self._getUniqueLdapAttribute(attrs, 'cn')
++ calendarUserAddresses = emailAddresses
++ enabledForCalendaring = True
++
++ return LdapDirectoryRecord(
++ service = self,
++ recordType = recordType,
++ guid = guid,
++ shortNames = shortNames,
++ authIDs = authIDs,
++ fullName = fullName,
++ firstName = firstName,
++ lastName = lastName,
++ emailAddresses = emailAddresses,
++ calendarUserAddresses = calendarUserAddresses,
++ enabledForCalendaring = enabledForCalendaring,
++ uid = uid,
++ dn = dn,
++ attrs = attrs,
++ )
++
++
++ def queryDirectory(self, recordTypes, indexType, indexKey):
++ self.log_debug("Querying directory for recordTypes %s, indexType %s and indexKey %s"
++ % (recordTypes, indexType, indexKey),
++ system="LdapDirectoryService")
++ for recordType in recordTypes:
++ # Build base for this record Type
++ base = ldap.dn.str2dn(self.rdnSchema[recordType]['rdn']) + ldap.dn.str2dn(self.rdnSchema['base'])
++
++ # Build filter
++ filter = "(!(objectClass=organizationalUnit))"
++ if self.rdnSchema[recordType]['filter']:
++ filter = "(&%s%s)" % (filter, self.rdnSchema[recordType]['filter'])
++ if indexType == self.INDEX_TYPE_GUID:
++ # Quey on guid only works if guid attribute has been defined.
++ # Support for query on guid even if is auto-generated should
++ # be added.
++ guidAttr = self.rdnSchema['guidAttr']
++ if not guidAttr: return
++ filter = "(&%s(%s=%s))" % (filter, guidAttr, indexKey)
++ elif indexType == self.INDEX_TYPE_SHORTNAME:
++ if recordType == self.recordType_users:
++ filter = "(&%s(|(uid=%s)(userid=%s)))" % (filter, indexKey, indexKey)
++ elif recordType in (self.recordType_groups, self.recordType_resources, self.recordType_locations):
++ filter = "(&%s(cn=%s))" % (filter, indexKey)
++ elif indexType == self.INDEX_TYPE_CUA:
++ # indexKey is of the form "mailto:test@example.net"
++ email = indexKey[7:] # strip "mailto:"
++ emailSuffix = self.rdnSchema[recordType]['emailSuffix']
++ if emailSuffix is not None and email.partition("@")[2] == emailSuffix:
++ filter = "(&%s(|(&(!(mail=*))(%s=%s))(mail=%s)))" % \
++ (filter, self.rdnSchema[recordType]['attr'],
++ email.partition("@")[0], email)
++ else:
++ filter = "(&%s(mail=%s))" % (filter, email)
++ elif indexType == self.INDEX_TYPE_AUTHID:
++ return
++
++ # Query the LDAP server
++ self.log_info("Retrieving ldap record with base %s and filter %s." %
++ (ldap.dn.dn2str(base), filter),
++ system="LdapDirectoryService")
++ result = self.ldap.search_s(ldap.dn.dn2str(base), ldap.SCOPE_SUBTREE, filter, self.attrList)
++
++ if result:
++ dn, attrs = result.pop()
++ self.recordCacheForType(recordType).addRecord(
++ self._ldapResultToRecord(dn, attrs, recordType),
++ indexType, indexKey
++ )
++
++
++
++class LdapDirectoryRecord(CachingDirectoryRecord):
++ """
++ LDAP implementation of L{IDirectoryRecord}.
++ """
++ def __init__(
++ self, service, recordType,
++ guid, shortNames, authIDs, fullName,
++ firstName, lastName, emailAddresses,
++ calendarUserAddresses, enabledForCalendaring, uid,
++ dn, attrs
++ ):
++ super(LdapDirectoryRecord, self).__init__(
++ service = service,
++ recordType = recordType,
++ guid = guid,
++ shortNames = shortNames,
++ authIDs = authIDs,
++ fullName = fullName,
++ firstName = firstName,
++ lastName = lastName,
++ emailAddresses = emailAddresses,
++ calendarUserAddresses = calendarUserAddresses,
++ enabledForCalendaring = enabledForCalendaring,
++ uid = uid,
++ )
++
++ # Save attributes of dn and attrs in case you might need them later
++ self.dn = dn
++ self.attrs = attrs
++
++ # Identifiers of the members of this record if it is a group
++ membersAttr = self.service.groupSchema['membersAttr']
++ self._memberIds = self.service._getMultipleLdapAttributes(attrs, membersAttr)
++
++ # Identifier of this record as a group member
++ memberIdAttr = self.service.groupSchema['memberIdAttr']
++ if memberIdAttr:
++ self._memberId = self.service._getUniqueLdapAttribute(attrs, memberIdAttr)
++ else:
++ self._memberId = self.dn
++
++
++ # Singleton with lazy loading
++ def _ldap(self):
++ try:
++ return self.ldap
++ except AttributeError:
++ # Use a different LDAP connection for authentication
++ # in order not to re-bind the server connection at each verification
++ self.log_info("Calling ldap.ldapobject.ReconnectLDAPObject(%s) in %s." % (repr(self.service.uri), repr(self)),
++ system="LdapDirectoryService")
++ self.ldap = ldap.ldapobject.ReconnectLDAPObject(self.service.uri)
++
++ if self.service.tlsCACertFile:
++ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.service.tlsCACertFile)
++ if self.service.tlsCACertDir:
++ ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, self.service.tlsCACertDir)
++
++ if self.service.tlsRequireCert == "never":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_NEVER)
++ elif self.service.tlsRequireCert == "allow":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_ALLOW)
++ elif self.service.tlsRequireCert == "try":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_TRY)
++ elif self.service.tlsRequireCert == "demand":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND)
++ elif self.service.tlsRequireCert == "hard":
++ self.ldap.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_HARD)
++
++ if self.service.tls:
++ self.ldap.start_tls_s()
++
++ return self.ldap
++
++ def _members(self):
++ # Only groups have members
++ memberIdAttr = self.service.groupSchema['memberIdAttr']
++ results = []
++ for memberId in self._memberIds:
++ if memberIdAttr:
++ base = self.service._base
++ filter = '(%s=%s)' % (self.service.groupSchema['memberIdAttr'], memberId)
++ self.log_info("Retrieving subtree of %s with filter %s." % (ldap.dn.dn2str(base), filter),
++ system="LdapDirectoryService")
++ result = self.service.ldap.search_s(ldap.dn.dn2str(base), ldap.SCOPE_SUBTREE, filter, self.service.attrList)
++ else:
++ self.log_info("Retrieving %s." % memberId, system="LdapDirectoryService")
++ result = self.service.ldap.search_s(memberId, ldap.SCOPE_BASE, attrlist=self.service.attrList)
++
++ assert len(result) == 1
++ dn, attrs = result.pop()
++
++ # Guess the recordType for the member (using scope bleeding)
++ for recordType in self.service.recordTypes():
++ attr = self.service.rdnSchema[recordType]['attr']
++ value = self.service._getUniqueLdapAttribute(attrs, attr)
++ calcDn = [[(attr, value, 1)]] + ldap.dn.str2dn(self.service.rdnSchema[recordType]['rdn']) \
++ + ldap.dn.str2dn(self.service.rdnSchema['base'])
++ if dn.lower() == ldap.dn.dn2str(calcDn).lower():
++ break
++
++ if recordType == self.service.recordType_users:
++ shortName = self.service._getUniqueLdapAttribute(attrs, 'uid', 'userid')
++ elif recordType in (self.service.recordType_groups, self.service.recordType_resources,
++ self.service.recordType_locations):
++ shortName = self.service._getUniqueLdapAttribute(attrs, 'cn')
++ results.append(self.service.recordWithShortName(recordType, shortName))
++
++ return results
++
++ def _groups(self):
++ recordType = self.service.recordType_groups
++ base = ldap.dn.str2dn(self.service.rdnSchema[recordType]['rdn']) + \
++ ldap.dn.str2dn(self.service.rdnSchema['base'])
++ filter = '(%s=%s)' % (self.service.groupSchema['membersAttr'], self._memberId)
++ self.log_info("Retrieving subtree of %s with filter %s." % (ldap.dn.dn2str(base), filter),
++ system="LdapDirectoryService")
++ results = self.service.ldap.search_s(ldap.dn.dn2str(base), ldap.SCOPE_SUBTREE, filter, self.service.attrList)
++
++ groups = []
++ for dn, attrs in results:
++ if recordType == self.service.recordType_users:
++ shortName = self.service._getUniqueLdapAttribute(attrs, 'uid', 'userid')
++ elif recordType in (self.service.recordType_groups, self.service.recordType_resources,
++ self.service.recordType_locations):
++ shortName = self.service._getUniqueLdapAttribute(attrs, 'cn')
++ groups.append(self.service.recordWithShortName(recordType, shortName))
++
++ return groups
++
++ # There is no need to implement a timeout here, the LdapDirectoryRecord objects
++ # themselves are destroyed and re-created when the records list is refreshed
++ # in LdapDirectoryService.
++
++ def members(self):
++ try:
++ return self._members_storage
++ except AttributeError:
++ self._members_storage = self._members()
++ return self._members_storage
++
++ def groups(self):
++ try:
++ return self._groups_storage
++ except AttributeError:
++ self._groups_storage = self._groups()
++ return self._groups_storage
++
++
++ # Credentials are checked against PAM
++ # Thus, the password is needed in clear-text and digest authentication
++ # can not be supported.
++ def verifyCredentials(self, credentials):
++ if isinstance(credentials, UsernamePassword):
++ # Check that the username supplied matches one of the shortNames
++ # (The DCS might already enforce this constraint, not sure)
++ if credentials.username not in self.shortNames:
++ return False
++
++ # Check cached password
++ try:
++ if credentials.password == self.password:
++ return True
++ except AttributeError:
++ pass
++
++ if not self.service.authMethod or self.service.authMethod.upper() == "PAM":
++ # Authenticate against PAM
++ def pam_conv(auth, query_list, userData):
++ return [(credentials.password, 0)]
++
++ auth = PAM.pam()
++ auth.start("caldav")
++ auth.set_item(PAM.PAM_USER, credentials.username)
++ auth.set_item(PAM.PAM_CONV, pam_conv)
++ try:
++ auth.authenticate()
++ except PAM.error, resp:
++ return False
++ else:
++ # Cache the password to avoid further LDAP queries
++ self.password = credentials.password
++ return True
++ elif self.service.authMethod.upper() == "LDAP":
++ # Authenticate against LDAP
++ try:
++ self.log_info("Calling LDAPObject.simple_bind_s(%s, [password])." % repr(self.dn), system="LdapDirectoryService")
++ self.log_info(self._ldap().bind_s(self.dn, credentials.password))
++ # Cache the password to avoid further LDAP queries
++ self.password = credentials.password
++ return True
++ except ldap.INVALID_CREDENTIALS:
++ return False
++ else:
++ err_msg = "Unknown Authentication Method '%s'" % (self.service.authMethod.upper())
++ self.log_error(err_msg)
++ raise err_msg
++
++ return super(LdapDirectoryRecord, self).verifyCredentials(credentials)
++
--- calendarserver-2.4.dfsg.orig/debian/patches/linux-xattr-fix.patch
+++ calendarserver-2.4.dfsg/debian/patches/linux-xattr-fix.patch
@@ -0,0 +1,182 @@
+diff -Naur calendarserver-2.4.dfsg.orig/calendarserver/tools/doublequotefix.py calendarserver-2.4.dfsg/calendarserver/tools/doublequotefix.py
+--- calendarserver-2.4.dfsg.orig/calendarserver/tools/doublequotefix.py 2009-01-06 00:27:18.000000000 +0530
++++ calendarserver-2.4.dfsg/calendarserver/tools/doublequotefix.py 2010-07-28 16:58:55.000000000 +0530
+@@ -56,14 +56,14 @@
+ def updateEtag(path, caldata):
+
+ x = xattr.xattr(path)
+- x["WebDAV:{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5"] = """<?xml version='1.0' encoding='UTF-8'?>
++ x["user.{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <getcontentmd5 xmlns='http://twistedmatrix.com/xml_namespace/dav/'>%s</getcontentmd5>
+ """ % (hashlib.md5(caldata).hexdigest(),)
+
+ def updateCtag(path):
+
+ x = xattr.xattr(path)
+- x["WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"] = """<?xml version='1.0' encoding='UTF-8'?>
++ x["user.{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <getctag xmlns='http://calendarserver.org/ns/'>%s</getctag>
+ """ % (str(datetime.datetime.now()),)
+
+diff -Naur calendarserver-2.4.dfsg.orig/calendarserver/tools/fixcalendardata.py calendarserver-2.4.dfsg/calendarserver/tools/fixcalendardata.py
+--- calendarserver-2.4.dfsg.orig/calendarserver/tools/fixcalendardata.py 2009-05-06 01:12:00.000000000 +0530
++++ calendarserver-2.4.dfsg/calendarserver/tools/fixcalendardata.py 2010-07-28 16:58:33.000000000 +0530
+@@ -61,14 +61,14 @@
+ def updateEtag(path, caldata):
+
+ x = xattr.xattr(path)
+- x["WebDAV:{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5"] = """<?xml version='1.0' encoding='UTF-8'?>
++ x["user.{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <getcontentmd5 xmlns='http://twistedmatrix.com/xml_namespace/dav/'>%s</getcontentmd5>
+ """ % (hashlib.md5(caldata).hexdigest(),)
+
+ def updateCtag(path):
+
+ x = xattr.xattr(path)
+- x["WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"] = """<?xml version='1.0' encoding='UTF-8'?>
++ x["user.{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <getctag xmlns='http://calendarserver.org/ns/'>%s</getctag>
+ """ % (str(datetime.datetime.now()),)
+
+@@ -101,7 +101,7 @@
+ for item in os.listdir(calendarHome):
+ calendarPath = os.path.join(calendarHome, item)
+ x = xattr.xattr(calendarPath)
+- if x.has_key("WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"):
++ if x.has_key("user.{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"):
+ scanCalendar(basePath, calendarPath, scanFile, doFix)
+
+ def scanCalendar(basePath, calendarPath, scanFile, doFix):
+diff -Naur calendarserver-2.4.dfsg.orig/contrib/tools/fix_calendar calendarserver-2.4.dfsg/contrib/tools/fix_calendar
+--- calendarserver-2.4.dfsg.orig/contrib/tools/fix_calendar 2008-06-24 02:02:56.000000000 +0530
++++ calendarserver-2.4.dfsg/contrib/tools/fix_calendar 2010-07-28 16:59:48.000000000 +0530
+@@ -44,14 +44,14 @@
+
+ # First fix the resourcetype & getctag on the calendar
+ x = xattr.xattr(path)
+- x["WebDAV:{DAV:}resourcetype"] = """<?xml version='1.0' encoding='UTF-8'?>
++ x["user.{DAV:}resourcetype"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <resourcetype xmlns='DAV:'>
+ <collection/>
+ <calendar xmlns='urn:ietf:params:xml:ns:caldav'/>
+ </resourcetype>
+ """
+
+- x["WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"] = """<?xml version='1.0' encoding='UTF-8'?>
++ x["user.{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <getctag xmlns='http://calendarserver.org/ns/'>Dummy Value</getctag>
+ """
+
+@@ -63,13 +63,13 @@
+
+ # getcontenttype
+ x = xattr.xattr(fullpath)
+- x["WebDAV:{DAV:}getcontenttype"] = """<?xml version='1.0' encoding='UTF-8'?>
++ x["user.{DAV:}getcontenttype"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <getcontenttype xmlns='DAV:'>text/calendar</getcontenttype>
+ """
+
+ # md5
+ data = open(fullpath).read()
+- x["WebDAV:{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5"] = """<?xml version='1.0' encoding='UTF-8'?>
++ x["user.{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <getcontentmd5 xmlns='http://twistedmatrix.com/xml_namespace/dav/'>%s</getcontentmd5>
+ """ % (hashlib.md5(data).hexdigest(),)
+
+@@ -103,4 +103,4 @@
+
+ except Exception, e:
+ sys.exit(str(e))
+-
+\ No newline at end of file
++
+diff -Naur calendarserver-2.4.dfsg.orig/twistedcaldav/admin/util.py calendarserver-2.4.dfsg/twistedcaldav/admin/util.py
+--- calendarserver-2.4.dfsg.orig/twistedcaldav/admin/util.py 2009-04-22 09:10:46.000000000 +0530
++++ calendarserver-2.4.dfsg/twistedcaldav/admin/util.py 2010-07-28 17:01:30.000000000 +0530
+@@ -92,7 +92,7 @@
+
+
+ def getPrincipalType(fp):
+- rtp = "WebDAV:" + RecordTypeProperty.sname().replace("/", "%2F")
++ rtp = "user." + RecordTypeProperty.sname().replace("/", "%2F")
+ x = xattr.xattr(fp.path)
+ if not x.has_key(rtp):
+ return None
+@@ -103,7 +103,7 @@
+ return rtp[0].firstChild().value
+
+ def getResourceType(fp):
+- rt = 'WebDAV:{DAV:}resourcetype'
++ rt = 'user.{DAV:}resourcetype'
+ x = xattr.xattr(fp.path)
+ if not x.has_key(rt):
+ return None
+@@ -173,8 +173,8 @@
+
+ from twisted.web2.dav.resource import TwistedQuotaRootProperty, TwistedQuotaUsedProperty
+
+-quotaRoot = "WebDAV:" + TwistedQuotaRootProperty.sname().replace("/", "%2F")
+-quotaUsed = "WebDAV:" + TwistedQuotaUsedProperty.sname().replace("/", "%2F")
++quotaRoot = "user." + TwistedQuotaRootProperty.sname().replace("/", "%2F")
++quotaUsed = "user." + TwistedQuotaUsedProperty.sname().replace("/", "%2F")
+
+ def getQuotaRoot(fp):
+ x = xattr.xattr(fp.path)
+diff -Naur calendarserver-2.4.dfsg.orig/twistedcaldav/test/data/makelargefbset.py calendarserver-2.4.dfsg/twistedcaldav/test/data/makelargefbset.py
+--- calendarserver-2.4.dfsg.orig/twistedcaldav/test/data/makelargefbset.py 2007-12-12 06:28:32.000000000 +0530
++++ calendarserver-2.4.dfsg/twistedcaldav/test/data/makelargefbset.py 2010-07-28 17:00:43.000000000 +0530
+@@ -45,7 +45,7 @@
+
+ inboxname = os.path.join(path, "inbox")
+ attrs = xattr.xattr(inboxname)
+- attrs["WebDAV:{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set"] = """<?xml version='1.0' encoding='UTF-8'?>
++ attrs["user.{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set"] = """<?xml version='1.0' encoding='UTF-8'?>
+ <calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'>
+ <href xmlns='DAV:'>/calendars/users/user%02d/calendar/</href>
+ <href xmlns='DAV:'>/calendars/users/user%02d/calendar.1000/</href>
+diff -Naur calendarserver-2.4.dfsg.orig/twistedcaldav/test/test_upgrade.py calendarserver-2.4.dfsg/twistedcaldav/test/test_upgrade.py
+--- calendarserver-2.4.dfsg.orig/twistedcaldav/test/test_upgrade.py 2009-11-19 01:35:55.000000000 +0530
++++ calendarserver-2.4.dfsg/twistedcaldav/test/test_upgrade.py 2010-07-28 17:00:20.000000000 +0530
+@@ -29,9 +29,9 @@
+ import hashlib
+ import os, zlib, cPickle
+
+-freeBusyAttr = "WebDAV:{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set"
+-cTagAttr = "WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"
+-md5Attr = "WebDAV:{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5"
++freeBusyAttr = "user.{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set"
++cTagAttr = "user.{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag"
++md5Attr = "user.{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5"
+
+
+ class ProxyDBUpgradeTests(TestCase):
+diff -Naur calendarserver-2.4.dfsg.orig/twistedcaldav/upgrade.py calendarserver-2.4.dfsg/twistedcaldav/upgrade.py
+--- calendarserver-2.4.dfsg.orig/twistedcaldav/upgrade.py 2009-11-19 01:21:02.000000000 +0530
++++ calendarserver-2.4.dfsg/twistedcaldav/upgrade.py 2010-07-28 17:01:06.000000000 +0530
+@@ -156,7 +156,7 @@
+
+ md5value = "<?xml version='1.0' encoding='UTF-8'?>\r\n<getcontentmd5 xmlns='http://twistedmatrix.com/xml_namespace/dav/'>%s</getcontentmd5>\r\n" % (hashlib.md5(data).hexdigest(),)
+ md5value = zlib.compress(md5value)
+- xattr.setxattr(resPath, "WebDAV:{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5", md5value)
++ xattr.setxattr(resPath, "user.{http:%2F%2Ftwistedmatrix.com%2Fxml_namespace%2Fdav%2F}getcontentmd5", md5value)
+
+ collectionUpdated = True
+
+@@ -164,7 +164,7 @@
+ if collectionUpdated:
+ ctagValue = "<?xml version='1.0' encoding='UTF-8'?>\r\n<getctag xmlns='http://calendarserver.org/ns/'>%s</getctag>\r\n" % (str(datetime.datetime.now()),)
+ ctagValue = zlib.compress(ctagValue)
+- xattr.setxattr(calPath, "WebDAV:{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag", ctagValue)
++ xattr.setxattr(calPath, "user.{http:%2F%2Fcalendarserver.org%2Fns%2F}getctag", ctagValue)
+
+ return errorOccurred
+
+@@ -194,7 +194,7 @@
+ # __uids__/<guid> form
+ if cal == "inbox":
+ for attr, value in xattr.xattr(calPath).iteritems():
+- if attr == "WebDAV:{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set":
++ if attr == "user.{urn:ietf:params:xml:ns:caldav}calendar-free-busy-set":
+ value = updateFreeBusySet(value, directory)
+ if value is not None:
+ # Need to write the xattr back to disk
--- calendarserver-2.4.dfsg.orig/debian/patches/nssdirectory.patch
+++ calendarserver-2.4.dfsg/debian/patches/nssdirectory.patch
@@ -0,0 +1,316 @@
+Index: calendarserver-2.4.dfsg/conf/caldavd.plist
+===================================================================
+--- calendarserver-2.4.dfsg.orig/conf/caldavd.plist 2010-08-24 21:17:17.000000000 +0530
++++ calendarserver-2.4.dfsg/conf/caldavd.plist 2010-08-24 21:50:01.000000000 +0530
+@@ -158,6 +158,39 @@
+ </dict>
+ -->
+
++ <!-- NSS Directory Service -->
++ <!-- Groups starting with groupPrefix are considered calendarserver groups -->
++ <!-- Don't treat user id's smaller than firstValidUid as calendarserver users -->
++ <!-- Don't treat group id's smaller than firstValidGid as calendarserver groups -->
++ <!-- use shortName@mailDomain as calender user mail addresses -->
++ <!--
++ <key>DirectoryService</key>
++ <dict>
++ <key>type</key>
++ <string>twistedcaldav.directory.nss.NssDirectoryService</string>
++
++ <key>params</key>
++ <dict>
++ <key>realmName</key>
++ <string>Test Realm</string>
++ <key>groupPrefix</key>
++ <string>caldavd-</string>
++ <key>firstValidUid</key>
++ <integer>1000</integer>
++ <key>lastValidUid</key>
++ <integer>65533</integer>
++ <key>firstValidGid</key>
++ <integer>1000</integer>
++ <key>lastValidGid</key>
++ <integer>65533</integer>
++ <key>mailDomain</key>
++ <string>example.com</string>
++ <key>cacheTimeout</key>
++ <integer>30</integer>
++ </dict>
++ </dict>
++ -->
++
+ <!--
+ Special principals
+
+Index: calendarserver-2.4.dfsg/twistedcaldav/directory/nss.py
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ calendarserver-2.4.dfsg/twistedcaldav/directory/nss.py 2010-08-24 21:51:04.000000000 +0530
+@@ -0,0 +1,267 @@
++##
++# vim: set fileencoding=utf-8 :
++# Copyright (c) 2008 Guido Guenther <agx@sigxcpu.org>
++#
++# Licensed under the Apache License, Version 2.0 (the "License");
++# you may not use this file except in compliance with the License.
++# You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++#
++##
++
++"""
++NSS Directory service interfaces.
++
++Uses libc's Name Service Switch for user and groups (/etc/nsswitch.conf).
++"""
++
++__all__ = [
++ "NssDirectoryService",
++]
++
++from twistedcaldav.directory.cachingdirectory import CachingDirectoryService,\
++ CachingDirectoryRecord
++from twisted.cred.credentials import UsernamePassword
++from twistedcaldav.scheduling.cuaddress import normalizeCUAddr
++from twisted.python import log
++import pwd, grp, socket
++import PAM
++
++class NsSwitch(object):
++ """Simple interface to the nsswitch calls"""
++
++ def get_user(self, username):
++ try:
++ return pwd.getpwnam(username)
++ except KeyError:
++ return None
++
++ def get_group(self, groupname):
++ try:
++ return grp.getgrnam(groupname)
++ except KeyError:
++ return None
++
++ def get_users(self):
++ return pwd.getpwall()
++
++ def get_groups(self):
++ return grp.getgrall()
++
++
++class NssDirectoryService(CachingDirectoryService):
++ """
++ Nss based Directory Service of L{IDirectoryService}
++ """
++
++ baseGUID = "8EFFFAF1-5221-4813-B971-58506B963573"
++
++ def __repr__(self):
++ return "<%s %r>" % (self.__class__.__name__, self.realmName)
++
++ # Defaults are in twistedcaldav.config:
++ def __init__(self, params, dosetup=True):
++ """
++ @param params: a dictionary containing the following keys:
++ cacheTimeout, realmName, groupPrefix, mailDomain, firstValidUid,
++ lastValidUid, firstValidGid, lastValidGid
++ @param dosetup: if C{True} then the directory records are initialized,
++ if C{False} they are not.
++ This should only be set to C{False} when doing unit tests.
++ """
++
++ defaults = {
++ "realmName": "Test Realm",
++ # we only consider groups starting with:
++ "groupPrefix": "caldavd-",
++ # dont set calendarUserAdresses by default
++ "mailDomain": None,
++ # exclude system users and nobody by "default":
++ "firstValidUid": 1000,
++ "lastValidUid": 65533,
++ "firstValidGid": 1000,
++ "lastValidGid": 65533,
++ "cacheTimeout": 30,
++ }
++ ignored = None
++ params = self.getParams(params, defaults, ignored)
++
++ super(NssDirectoryService, self).__init__(params['cacheTimeout'])
++
++ self.nsswitch = NsSwitch()
++ self.realmName = params["realmName"]
++ self.mailDomain = params["mailDomain"]
++ self.groupPrefix = params["groupPrefix"]
++ self.first_valid_uid = params["firstValidUid"]
++ self.first_valid_gid = params["firstValidGid"]
++ self.last_valid_uid = params["lastValidUid"]
++ self.last_valid_gid = params["lastValidGid"]
++
++ def recordTypes(self):
++ recordTypes = (
++ self.recordType_users,
++ self.recordType_groups,
++ )
++ return recordTypes
++
++ def _isValidUid(self, uid):
++ if uid >= self.first_valid_uid and uid <= self.last_valid_uid:
++ return True
++
++ def _isValidGid(self, gid):
++ if gid >= self.first_valid_gid and gid <= self.last_valid_gid:
++ return True
++
++ def queryDirectory(self, recordTypes, indexType, indexKey):
++ self.log_debug("Querying directory for recordTypes %s, "
++ "indexType %s and indexKey %s" %
++ (recordTypes, indexType, indexKey),
++ system="NssDirectoryService")
++
++ def _recordWithGUID(recordType, guid):
++ # Code has to be written to query on GUID
++ pass
++
++ def _recordWithShortName(recordType, shortName):
++ record = None
++ if recordType == self.recordType_users:
++ result = self.nsswitch.get_user(shortName)
++ if result and self._isValidUid(result[2]):
++ record = NssUserRecord(
++ service = self,
++ userName = result[0],
++ gecos = result[4],
++ )
++ elif recordType == self.recordType_groups:
++ result = self.nsswitch.get_group(self.groupPrefix + shortName)
++ if result and self._isValidGid(result[2]):
++ record = NssGroupRecord(
++ service = self,
++ groupName = result[0],
++ members = result[3]
++ )
++ return record
++
++ for recordType in recordTypes:
++ record = None
++ if indexType == self.INDEX_TYPE_GUID:
++ record = _recordWithGUID(recordType, indexKey)
++ elif indexType == self.INDEX_TYPE_SHORTNAME:
++ record = _recordWithShortName(recordType, indexKey)
++ elif indexType == self.INDEX_TYPE_CUA:
++ address = normalizeCUAddr(indexKey)
++ if address.startswith("urn:uuid:"):
++ guid = address[9:]
++ record = _recordWithGUID(recordType, guid)
++ elif address.startswith("mailto:") and \
++ address.endswith("@"+self.mailDomain):
++ shortName = address[7:].partition("@")[0]
++ record = _recordWithShortName(recordType, shortName)
++ elif indexType == self.INDEX_TYPE_AUTHID:
++ pass
++
++ if record:
++ self.recordCacheForType(recordType).addRecord(
++ record, indexType, indexKey
++ )
++
++
++class NssDirectoryRecord(CachingDirectoryRecord):
++ """
++ Nss Directory Record
++ """
++ def __init__(self, service, recordType, shortNames,
++ fullName=None, calendarUserAddresses=set()):
++ super(NssDirectoryRecord, self).__init__(
++ service = service,
++ recordType = recordType,
++ guid = None,
++ shortNames = shortNames,
++ fullName = fullName,
++ calendarUserAddresses = calendarUserAddresses,
++ )
++
++
++class NssUserRecord(NssDirectoryRecord):
++ """
++ NSS Users implementation of L{IDirectoryRecord}.
++ """
++ def __init__(self, service, userName, gecos):
++ recordType = service.recordType_users
++ shortNames = (userName,)
++ fullName = gecos.split(",",1)[0]
++ calendarUserAddresses = set()
++ if service.mailDomain:
++ calendarUserAddresses.add("mailto:%s@%s" %
++ (userName, service.mailDomain))
++ super(NssUserRecord, self).__init__(service, recordType, shortNames,
++ fullName, calendarUserAddresses)
++
++ def groups(self):
++ for result in self.service.nsswitch.get_groups():
++ if self.service._isValidGid(result[2]) and \
++ result[0].startswith(self.service.groupPrefix) and \
++ self.shortNames[0] in result[3]:
++ yield self.service.recordWithShortName(
++ self.service.recordType_groups,
++ result[0].replace(self.service.groupPrefix,'',1)
++ )
++
++ def verifyCredentials(self, credentials):
++ if isinstance(credentials, UsernamePassword):
++ # Check that the username supplied matches the shortName
++ # (The DCS might already enforce this constraint, not sure)
++ if credentials.username not in self.shortNames:
++ return False
++
++ # Check cached password
++ try:
++ if credentials.password == self.password:
++ return True
++ except AttributeError:
++ pass
++
++ # Authenticate against PAM
++ def pam_conv(auth, query_list, userData):
++ return [(credentials.password, 0)]
++
++ auth = PAM.pam()
++ auth.start("caldav")
++ auth.set_item(PAM.PAM_USER, credentials.username)
++ auth.set_item(PAM.PAM_CONV, pam_conv)
++ try:
++ auth.authenticate()
++ except PAM.error, resp:
++ return False
++ else:
++ # Cache the password to avoid future DS queries
++ self.password = credentials.password
++ return True
++
++ return super(NssUserRecord, self).verifyCredentials(credentials)
++
++class NssGroupRecord(NssDirectoryRecord):
++ """
++ NSS Groups implementation of L{IDirectoryRecord}.
++ """
++ def __init__(self, service, groupName, members=()):
++ recordType = service.recordType_groups
++ shortNames = (groupName.replace(service.groupPrefix,'',1),)
++ super(NssGroupRecord, self).__init__(service, recordType, shortNames)
++ self._members = members
++
++ def members(self):
++ for shortName in self._members:
++ yield self.service.recordWithShortName(
++ self.service.recordType_users,
++ shortName
++ )
++
++# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
--- calendarserver-2.4.dfsg.orig/debian/patches/series
+++ calendarserver-2.4.dfsg/debian/patches/series
@@ -0,0 +1,5 @@
+nssdirectory.patch
+ldapdirectory.patch
+paths.diff
+linux-xattr-fix.patch
+secure-python-path.patch