forked from friendica/friendica-addons
Merge pull request #57 from CatoTH/master
New Addon: Calendar with CalDAV support
This commit is contained in:
commit
915145fc27
562 changed files with 189520 additions and 4 deletions
31
dav/FriendicaACLPlugin.inc.php
Normal file
31
dav/FriendicaACLPlugin.inc.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
class Sabre_DAVACL_Plugin_Friendica extends Sabre_DAVACL_Plugin {
|
||||
|
||||
/*
|
||||
* A dirty hack to make iOS CalDAV work with subdirectorys.
|
||||
* When using a Root URL like /dav/ (as it is necessary for friendica), iOS does not evaluate the current-user-principal property,
|
||||
* but only principal-URL. Actually principal-URL is not allowed in /dav/, only for Principal collections, but this seems
|
||||
* to be the only way to force iOS to look at the right location.
|
||||
*/
|
||||
|
||||
public function beforeGetProperties($uri, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
|
||||
|
||||
parent::beforeGetProperties($uri, $node, $requestedProperties, $returnedProperties);
|
||||
|
||||
if (false !== ($index = array_search('{DAV:}principal-URL', $requestedProperties))) {
|
||||
|
||||
unset($requestedProperties[$index]);
|
||||
$returnedProperties[200]['{DAV:}principal-URL'] = new Sabre_DAV_Property_Href('principals/users/' . strtolower($_SERVER["PHP_AUTH_USER"]) . '/');
|
||||
|
||||
}
|
||||
if (false !== ($index = array_search('{urn:ietf:params:xml:ns:caldav}calendar-home-set', $requestedProperties))) {
|
||||
|
||||
unset($requestedProperties[$index]);
|
||||
$returnedProperties[200]['{urn:ietf:params:xml:ns:caldav}calendar-home-set'] = new Sabre_DAV_Property_Href('calendars/' . strtolower($_SERVER["PHP_AUTH_USER"]) . '/');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
71
dav/README.md
Normal file
71
dav/README.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
Calendar with CalDAV Support
|
||||
|
||||
This is a rewrite of the calendar system used by the german social network [Animexx](http://www.animexx.de/).
|
||||
It's still in a very early stage, so expect major bugs. Please feel free to report any of them, by mail (cato@animexx.de) or Friendica: http://friendica.hoessl.eu/profile/cato
|
||||
|
||||
At the moment, the calendar system supports the following features:
|
||||
- A web-based drag&drop interface for managing events
|
||||
- All-Day-Events, Multi-Day-Events, and time-based events
|
||||
- Access to the events using CalDAV (using iPhone, Thunderbird Lightning etc., see below)
|
||||
- read-only access to the friendica-native events (also using CalDAV)
|
||||
- The friendica-contacts are made available using CardDAV (confirmed to work with iOS)
|
||||
- Giving the subject, a description, a location and a color for the event (the color is not available through CalDAV, though)
|
||||
|
||||
Internationalization:
|
||||
- At the moment, settings for the US and the german systems are selectable (regarding the date format and the first day of the week). More will be added on request.
|
||||
- The basic design of the system is aware of timezones; however this is not reflected in the UI yet. It currently assumes that the timezone set in the friendica-installation matches the user's local time and matches the local time set in the user's operating system.
|
||||
|
||||
CalDAV device compatibility:
|
||||
- iOS (iPhone/iPodTouch) works
|
||||
- Thunderbird Lightning should work, not tested yet
|
||||
- Android: http://dmfs.org/caldav/ seems to work, not much tested yet, though
|
||||
|
||||
Installation
|
||||
After activating, serveral tables in the database have to be created. The admin-interface of the plugin will try to do this automatically.
|
||||
In case of errors, the SQL-statement to create the tables manually are shown in the admin-interface.
|
||||
|
||||
|
||||
Functuality missing: (a.k.a. "Roadmap")
|
||||
- Recurrence of events (this is only supported using the CalDAV-interface; recurring events saved using CalDAV will appear correctly multiple times in the web-based frontend; hovever those events will be read-only at the web-based frondend)
|
||||
- Sharing events; all events are private at the moment, therefore this system is not yet a complete replacement for the friendica-native events
|
||||
- Attendees / Collaboration
|
||||
|
||||
|
||||
|
||||
Used libraries
|
||||
|
||||
SabreDAV
|
||||
http://code.google.com/p/sabredav/
|
||||
New BSD License
|
||||
|
||||
wdCalendar
|
||||
http://www.web-delicious.com/jquery-plugins/
|
||||
GNU Lesser General Public License
|
||||
|
||||
jQueryUI
|
||||
http://jqueryui.com/
|
||||
Dual-licenced: MIT and GPL licenses
|
||||
|
||||
iCalCreator
|
||||
http://kigkonsult.se/iCalcreator/
|
||||
GNU Lesser General Public License
|
||||
|
||||
TimePicker
|
||||
http://www.texotela.co.uk/code/jquery/timepicker/
|
||||
Dual-licenced: MIT and GPL licenses
|
||||
|
||||
ColorPicker
|
||||
http://laktek.com/2008/10/27/really-simple-color-picker-in-jquery/
|
||||
MIT License
|
||||
|
||||
|
||||
|
||||
Author of this plugin (the parts that are not part of the libraries above):
|
||||
Tobias Hößl
|
||||
http://friendica.hoessl.eu/profile/cato
|
||||
http://www.hoessl.eu/
|
||||
tobias@hoessl.eu
|
||||
@TobiasHoessl
|
||||
|
||||
Originally developed for:
|
||||
Animexx e.V. / http://www.animexx.de/
|
879
dav/SabreDAV/ChangeLog
Normal file
879
dav/SabreDAV/ChangeLog
Normal file
|
@ -0,0 +1,879 @@
|
|||
1.7.0-alpha (2012-??-??)
|
||||
* BC Break: The calendarobjects database table has a bunch of new fields,
|
||||
and a migration script is required to ensure everything will keep
|
||||
working. Read the wiki for more details.
|
||||
* BC Break: The iCalendar interface now has a new method: calendarQuery.
|
||||
* BC Break: In this version a number of classes have been deleted, that
|
||||
have been previously deprecated. Namely:
|
||||
- Sabre_DAV_Directory (now: Sabre_DAV_Collection)
|
||||
- Sabre_DAV_SimpleDirectory (now: Sabre_DAV_SimpleCollection)
|
||||
- Sabre_VObject_Element_DateTime (now: Sabre_VObject_Property_DateTime)
|
||||
- Sabre_VObject_Element_MultiDateTime (now .._Property_MultiDateTime)
|
||||
* BC Break: Sabre_CalDAV_Schedule_IMip::sendMessage now has an extra
|
||||
argument. If you extended this class, you should fix this method. It's
|
||||
only used for informational purposes.
|
||||
* Changed: Responsibility for dealing with the calendar-query is now moved
|
||||
from the CalDAV plugin to the CalDAV backends. This allows for heavy
|
||||
optimizations.
|
||||
* Changed: The CalDAV PDO backend is now a lot faster for common calendar
|
||||
queries.
|
||||
* Fixed: Marking both the text/calendar and text/x-vcard as UTF-8 encoded.
|
||||
* Fixed: Workaround for the SOGO connector, as it doesn't understand
|
||||
receiving "text/x-vcard; charset=utf-8" for a contenttype.
|
||||
* Added: Sabre_DAV_Client now throws more specific exceptions in cases
|
||||
where we already has an exception class.
|
||||
* Added: Sabre_DAV_PartialUpdate. This plugin allows you to use the PATCH
|
||||
method to update parts of a file.
|
||||
|
||||
1.6.3-stable (2012-??-??)
|
||||
* Added: It's now possible to specify in Sabre_DAV_Client which type of
|
||||
authentication is to be used.
|
||||
* Fixed: Issue 206: Sabre_DAV_Client PUT requests are fixed.
|
||||
* Fixed: Issue 205: Parsing an iCalendar 0-second date interval.
|
||||
* Fixed: Issue 112: Stronger validation of iCalendar objects. Now making
|
||||
sure every iCalendar object only contains 1 component, and disallowing
|
||||
vcards, forcing every component to have a UID.
|
||||
* Fixed: Basic validation for vcards in the CardDAV plugin.
|
||||
* Fixed: Issue 213: Workaround for an Evolution bug, that prevented it
|
||||
from updating events.
|
||||
* Fixed: Issue 211: A time-limit query on a non-relative alarm trigger in
|
||||
a recurring event could result in an endless loop.
|
||||
* Fixed: All uri fields are now a maximum of 200 characters. The Bynari
|
||||
outlook plugin used much longer strings so this should improve
|
||||
compatibility.
|
||||
* Fixed: Added a workaround for a bug in KDE 4.8.2 contact syncing. See
|
||||
https://bugs.kde.org/show_bug.cgi?id=300047
|
||||
|
||||
1.6.2-stable (2012-04-16)
|
||||
* Fixed: Sabre_VObject_Node::$parent should have been public.
|
||||
* Fixed: Recurrence rules of events are now taken into consideration when
|
||||
doing time-range queries on alarms.
|
||||
* Fixed: Added a workaround for the fact that php's DateInterval cannot
|
||||
parse weeks and days at the same time.
|
||||
* Added: Sabre_DAV_Server::$exposeVersion, allowing you to hide SabreDAV's
|
||||
version number from various outputs.
|
||||
* Fixed: DTSTART values would be incorrect when expanding events.
|
||||
* Fixed: DTSTART and DTEND would be incorrect for expansion of WEEKLY
|
||||
BYDAY recurrences.
|
||||
* Fixed: Issue 203: A problem with overridden events hitting the exact
|
||||
date and time of a subsequent event in the recurrence set.
|
||||
* Fixed: There was a problem with recurrence rules, for example the 5th
|
||||
tuesday of the month, if this day did not exist.
|
||||
* Added: New HTTP status codes from draft-nottingham-http-new-status-04.
|
||||
|
||||
1.6.1-stable (2012-03-05)
|
||||
* Added: createFile and put() can now return an ETag.
|
||||
* Added: Sending back an ETag on for operations on CardDAV backends. This
|
||||
should help with OS X 10.6 Addressbook compatibility.
|
||||
* Fixed: Fixed a bug where an infinite loop could occur in the recurrence
|
||||
iterator if the recurrence was YEARLY, with a BYMONTH rule, and either
|
||||
BYDAY or BYMONTHDAY match the first day of the month.
|
||||
* Fixed: Events that are excluded using EXDATE are still counted in the
|
||||
COUNT= parameter in the RRULE property.
|
||||
* Added: Support for time-range filters on VALARM components.
|
||||
* Fixed: Correctly filtering all-day events.
|
||||
* Fixed: Sending back correct mimetypes from the browser plugin (thanks
|
||||
Jürgen).
|
||||
* Fixed: Issue 195: Sabre_CardDAV pear package had an incorrect dependency.
|
||||
* Fixed: Calendardata would be destroyed when performing a MOVE request.
|
||||
|
||||
1.6.0-stable (2012-02-22)
|
||||
* BC Break: Now requires PHP 5.3
|
||||
* BC Break: Any node that implemented Sabre_DAVACL_IACL must now also
|
||||
implement the getSupportedPrivilegeSet method. See website for details.
|
||||
* BC Break: Moved functions from Sabre_CalDAV_XMLUtil to
|
||||
Sabre_VObject_DateTimeParser.
|
||||
* BC Break: The Sabre_DAVACL_IPrincipalCollection now has two new methods:
|
||||
'searchPrincipals' and 'updatePrincipal'.
|
||||
* BC Break: Sabre_DAV_ILockable is removed and all related per-node
|
||||
locking functionality.
|
||||
* BC Break: Sabre_DAV_Exception_FileNotFound is now deprecated in favor of
|
||||
Sabre_DAV_Exception_NotFound. The former will be removed in a later
|
||||
version.
|
||||
* BC Break: Removed Sabre_CalDAV_ICalendarUtil, use Sabre_VObject instead.
|
||||
* BC Break: Sabre_CalDAV_Server is now deprecated, check out the
|
||||
documentation on how to setup a caldav server with just
|
||||
Sabre_DAV_Server.
|
||||
* BC Break: Default Principals PDO backend now needs a new field in the
|
||||
'principals' table. See the website for details.
|
||||
* Added: Ability to create new calendars and addressbooks from within the
|
||||
browser plugin.
|
||||
* Added: Browser plugin: icons for various nodes.
|
||||
* Added: Support for FREEBUSY reports!
|
||||
* Added: Support for creating principals with admin-level privileges.
|
||||
* Added: Possibility to let server send out invitation emails on behalf of
|
||||
CalDAV client, using Sabre_CalDAV_Schedule_IMip.
|
||||
* Changed: beforeCreateFile event now passes data argument by reference.
|
||||
* Changed: The 'propertyMap' property from Sabre_VObject_Reader, must now
|
||||
be specified in Sabre_VObject_Property::$classMap.
|
||||
* Added: Ability for plugins to tell the ACL plugin which principal
|
||||
plugins are searchable.
|
||||
* Added: [DAVACL] Per-node overriding of supported privileges. This allows
|
||||
for custom privileges where needed.
|
||||
* Added: [DAVACL] Public 'principalSearch' method on the DAVACL plugin,
|
||||
which allows for easy searching for principals, based on their
|
||||
properties.
|
||||
* Added: Sabre_VObject_Component::getComponents() to return a list of only
|
||||
components and not properties.
|
||||
* Added: An includes.php file in every sub-package (CalDAV, CardDAV, DAV,
|
||||
DAVACL, HTTP, VObject) as an alternative to the autoloader. This often
|
||||
works much faster.
|
||||
* Added: Support for the 'Me card', which allows Addressbook.app users
|
||||
specify which vcard is their own.
|
||||
* Added: Support for updating principal properties in the DAVACL principal
|
||||
backends.
|
||||
* Changed: Major refactoring in the calendar-query REPORT code. Should
|
||||
make things more flexible and correct.
|
||||
* Changed: The calendar-proxy-[read|write] principals will now only appear
|
||||
in the tree, if they actually exist in the Principal backend. This should
|
||||
reduce some problems people have been having with this.
|
||||
* Changed: Sabre_VObject_Element_* classes are now renamed to
|
||||
Sabre_VObject_Property. Old classes are retained for backwards
|
||||
compatibility, but this will be removed in the future.
|
||||
* Added: Sabre_VObject_FreeBusyGenerator to generate free-busy reports
|
||||
based on lists of events.
|
||||
* Added: Sabre_VObject_RecurrenceIterator to find all the dates and times
|
||||
for recurring events.
|
||||
* Fixed: Issue 97: Correctly handling RRULE for the calendar-query REPORT.
|
||||
* Fixed: Issue 154: Encoding of VObject parameters with no value was
|
||||
incorrect.
|
||||
* Added: Support for {DAV:}acl-restrictions property from RFC3744.
|
||||
* Added: The contentlength for calendar objects can now be supplied by a
|
||||
CalDAV backend, allowing for more optimizations.
|
||||
* Fixed: Much faster implementation of Sabre_DAV_URLUtil::encodePath.
|
||||
* Fixed: {DAV:}getcontentlength may now be not specified.
|
||||
* Fixed: Issue 66: Using rawurldecode instead of urldecode to decode paths
|
||||
from clients. This means that + will now be treated as a literal rather
|
||||
than a space, and this should improve compatibility with the Windows
|
||||
built-in client.
|
||||
* Added: Sabre_DAV_Exception_PaymentRequired exception, to emit HTTP 402
|
||||
status codes.
|
||||
* Added: Some mysql unique constraints to example files.
|
||||
* Fixed: Correctly formatting HTTP dates.
|
||||
* Fixed: Issue 94: Sending back Last-Modified header for 304 responses.
|
||||
* Added: Sabre_VObject_Component_VEvent, Sabre_VObject_Component_VJournal,
|
||||
Sabre_VObject_Component_VTodo and Sabre_VObject_Component_VCalendar.
|
||||
* Changed: Properties are now also automatically mapped to their
|
||||
appropriate classes, if they are created using the add() or __set()
|
||||
methods.
|
||||
* Changed: Cloning VObject objects now clones the entire tree, rather than
|
||||
just the default shallow copy.
|
||||
* Added: Support for recurrence expansion in the CALDAV:calendar-multiget
|
||||
and CALDAV:calendar-query REPORTS.
|
||||
* Changed: CalDAV PDO backend now sorts calendars based on the internal
|
||||
'calendarorder' field.
|
||||
* Added: Issue 181: Carddav backends may no optionally not supply the carddata in
|
||||
getCards, if etag and size are specified. This may speed up certain
|
||||
requests.
|
||||
* Added: More arguments to beforeWriteContent and beforeCreateFile (see
|
||||
WritingPlugins wiki document).
|
||||
* Added: Hook for iCalendar validation. This allows us to validate
|
||||
iCalendar objects when they're uploaded. At the moment we're just
|
||||
validating syntax.
|
||||
* Added: VObject now support Windows Timezone names correctly (thanks
|
||||
mrpace2).
|
||||
* Added: If a timezonename could not be detected, we fall back on the
|
||||
default PHP timezone.
|
||||
* Added: Now a Composer package (thanks willdurand).
|
||||
* Fixed: Support for \N as a newline character in the VObject reader.
|
||||
* Added: afterWriteContent, afterCreateFile and afterUnbind events.
|
||||
* Added: Postgresql example files. Not part of the unittests though, so
|
||||
use at your own risk.
|
||||
* Fixed: Issue 182: Removed backticks from sql queries, so it will work
|
||||
with Postgres.
|
||||
|
||||
1.5.9-stable (2012-04-16)
|
||||
* Fixed: Issue with parsing timezone identifiers that were surrounded by
|
||||
quotes. (Fixes emClient compatibility).
|
||||
|
||||
1.5.8-stable (2012-02-22)
|
||||
* Fixed: Issue 95: Another timezone parsing issue, this time in
|
||||
calendar-query.
|
||||
|
||||
1.5.7-stable (2012-02-19)
|
||||
* Fixed: VObject properties are now always encoded before components.
|
||||
* Fixed: Sabre_DAVACL had issues with multiple levels of privilege
|
||||
aggregration.
|
||||
* Changed: Added 'GuessContentType' plugin to fileserver.php example.
|
||||
* Fixed: The Browser plugin will now trigger the correct events when
|
||||
creating files.
|
||||
* Fixed: The ICSExportPlugin now considers ACL's.
|
||||
* Added: Made it optional to supply carddata from an Addressbook backend
|
||||
when requesting getCards. This can make some operations much faster, and
|
||||
could result in much lower memory use.
|
||||
* Fixed: Issue 187: Sabre_DAV_UUIDUtil was missing from includes file.
|
||||
* Fixed: Issue 191: beforeUnlock was triggered twice.
|
||||
|
||||
1.5.6-stable (2012-01-07)
|
||||
* Fixed: Issue 174: VObject could break UTF-8 characters.
|
||||
* Fixed: pear package installation issues.
|
||||
|
||||
1.5.5-stable (2011-12-16)
|
||||
* Fixed: CalDAV time-range filter workaround for recurring events.
|
||||
* Fixed: Bug in Sabre_DAV_Locks_Backend_File that didn't allow multiple
|
||||
files to be locked at the same time.
|
||||
|
||||
1.5.4-stable (2011-10-28)
|
||||
* Fixed: GuessContentType plugin now supports mixed case file extensions.
|
||||
* Fixed: DATE-TIME encoding was wrong in VObject. (we used 'DATETIME').
|
||||
* Changed: Sending back HTTP 204 after a PUT request on an existing resource
|
||||
instead of HTTP 200. This should fix Evolution CardDAV client
|
||||
compatibility.
|
||||
* Fixed: Issue 95: Parsing X-LIC-LOCATION if it's available.
|
||||
* Added: All VObject elements now have a reference to their parent node.
|
||||
|
||||
1.5.3-stable (2011-09-28)
|
||||
* Fixed: Sabre_DAV_Collection was missing from the includes file.
|
||||
* Fixed: Issue 152. iOS 1.4.2 apparantly requires HTTP/1.1 200 OK to be in
|
||||
uppercase.
|
||||
* Fixed: Issue 153: Support for files with mixed newline styles in
|
||||
Sabre_VObject.
|
||||
* Fixed: Issue 159: Automatically converting any vcard and icalendardata
|
||||
to UTF-8.
|
||||
* Added: Sabre_DAV_SimpleFile class for easy static file creation.
|
||||
* Added: Issue 158: Support for the CARDDAV:supported-address-data
|
||||
property.
|
||||
|
||||
1.5.2-stable (2011-09-21)
|
||||
* Fixed: carddata and calendardata MySQL fields are now of type
|
||||
'mediumblob'. 'TEXT' was too small sometimes to hold all the data.
|
||||
* Fixed: {DAV:}supported-report-set is now correctly reporting the reports
|
||||
for IAddressBook.
|
||||
* Added: Sabre_VObject_Property::add() to add duplicate parameters to
|
||||
properties.
|
||||
* Added: Issue 151: Sabre_CalDAV_ICalendar and Sabre_CalDAV_ICalendarObject
|
||||
interfaces.
|
||||
* Fixed: Issue 140: Not returning 201 Created if an event cancelled the
|
||||
creation of a file.
|
||||
* Fixed: Issue 150: Faster URLUtil::encodePath() implementation.
|
||||
* Fixed: Issue 144: Browser plugin could interfere with
|
||||
TemporaryFileFilterPlugin if it was loaded first.
|
||||
* Added: It's not possible to specify more 'alternate uris' in principal
|
||||
backends.
|
||||
|
||||
1.5.1-stable (2011-08-24)
|
||||
* Fixed: Issue 137. Hiding action interface in HTML browser for
|
||||
non-collections.
|
||||
* Fixed: addressbook-query is now correctly returned from the
|
||||
{DAV:}supported-report-set property.
|
||||
* Fixed: Issue 142: Bugs in groupwareserver.php example.
|
||||
* Fixed: Issue 139: Rejecting PUT requests with Content-Range.
|
||||
|
||||
1.5.0-stable (2011-08-12)
|
||||
* Added: CardDAV support.
|
||||
* Added: An experimental WebDAV client.
|
||||
* Added: MIME-Directory grouping support in the VObject library. This is
|
||||
very useful for people attempting to parse vcards.
|
||||
* BC Break: Adding parameters with the VObject libraries now overwrites
|
||||
the previous parameter, rather than just add it. This makes more sense
|
||||
for 99% of the cases.
|
||||
* BC Break: lib/Sabre.autoload.php is now removed in favor of
|
||||
lib/Sabre/autoload.php.
|
||||
* Deprecated: Sabre_DAV_Directory is now deprecated and will be removed in
|
||||
a future version. Use Sabre_DAV_Collection instead.
|
||||
* Deprecated: Sabre_DAV_SimpleDirectory is now deprecated and will be
|
||||
removed in a future version. Use Sabre_DAV_SimpleCollection instead.
|
||||
* Fixed: Problem with overriding tablenames for the CalDAV backend.
|
||||
* Added: Clark-notation parser to XML utility.
|
||||
* Added: unset() support to VObject components.
|
||||
* Fixed: Refactored CalDAV property fetching to be faster and simpler.
|
||||
* Added: Central string-matcher for CalDAV and CardDAV plugins.
|
||||
* Added: i;unicode-casemap support
|
||||
* Fixed: VObject bug: wouldn't parse parameters if they weren't specified
|
||||
in uppercase.
|
||||
* Fixed: VObject bug: Parameters now behave more like Properties.
|
||||
* Fixed: VObject bug: Parameters with no value are now correctly parsed.
|
||||
* Changed: If calendars don't specify which components they allow, 'all'
|
||||
components are assumed (e.g.: VEVENT, VTODO, VJOURNAL).
|
||||
* Changed: Browser plugin now uses POST variable 'sabreAction' instead of
|
||||
'action' to reduce the chance of collisions.
|
||||
|
||||
1.4.4-stable (2011-07-07)
|
||||
* Fixed: Issue 131: Custom CalDAV backends could break in certain cases.
|
||||
* Added: The option to override the default tablename all PDO backends
|
||||
use. (Issue 60).
|
||||
* Fixed: Issue 124: 'File' authentication backend now takes realm into
|
||||
consideration.
|
||||
* Fixed: Sabre_DAV_Property_HrefList now properly deserializes. This
|
||||
allows users to update the {DAV:}group-member-set property.
|
||||
* Added: Helper functions for DateTime-values in Sabre_VObject package.
|
||||
* Added: VObject library can now automatically map iCalendar properties to
|
||||
custom classes.
|
||||
|
||||
1.4.3-stable (2011-04-25)
|
||||
* Fixed: Issue 123: Added workaround for Windows 7 UNLOCK bug.
|
||||
* Fixed: datatype of lastmodified field in mysql.calendars.sql. Please
|
||||
change the DATETIME field to an INT to ensure this field will work
|
||||
correctly.
|
||||
* Change: Sabre_DAV_Property_Principal is now renamed to
|
||||
Sabre_DAVACL_Property_Principal.
|
||||
* Added: API level support for ACL HTTP method.
|
||||
* Fixed: Bug in serializing {DAV:}acl property.
|
||||
* Added: deserializer for {DAV:}resourcetype property.
|
||||
* Added: deserializer for {DAV:}acl property.
|
||||
* Added: deserializer for {DAV:}principal property.
|
||||
|
||||
1.4.2-beta (2011-04-01)
|
||||
* Added: It's not possible to disable listing of nodes that are denied
|
||||
read access by ACL.
|
||||
* Fixed: Changed a few properties in CalDAV classes from private to
|
||||
protected.
|
||||
* Fixed: Issue 119: Terrible things could happen when relying on
|
||||
guessBaseUri, the server was running on the root of the domain and a user
|
||||
tried to access a file ending in .php. This is a slight BC break.
|
||||
* Fixed: Issue 118: Lock tokens in If headers without a uri should be
|
||||
treated as the request uri, not 'all relevant uri's.
|
||||
* Fixed: Issue 120: PDO backend was incorrectly fetching too much locks in
|
||||
cases where there were similar named locked files in a directory.
|
||||
|
||||
1.4.1-beta (2011-02-26)
|
||||
* Fixed: Sabre_DAV_Locks_Backend_PDO returned too many locks.
|
||||
* Fixed: Sabre_HTTP_Request::getHeader didn't return Content-Type when
|
||||
running on apache, so a few workarounds were added.
|
||||
* Change: Slightly changed CalDAV Backend API's, to allow for heavy
|
||||
optimizations. This is non-bc breaking.
|
||||
|
||||
1.4.0-beta (2011-02-12)
|
||||
* Added: Partly RFC3744 ACL support.
|
||||
* Added: Calendar-delegation (caldav-proxy) support.
|
||||
* BC break: In order to fix Issue 99, a new argument had to be added to
|
||||
Sabre_DAV_Locks_Backend_*::getLocks classes. Consult the classes for
|
||||
details.
|
||||
* Deprecated: Sabre_DAV_Locks_Backend_FS is now deprecated and will be
|
||||
removed in a later version. Use PDO or the new File class instead.
|
||||
* Deprecated: The Sabre_CalDAV_ICalendarUtil class is now marked
|
||||
deprecated, and will be removed in a future version. Please use
|
||||
Sabre_VObject instead.
|
||||
* Removed: All principal-related functionality has been removed from the
|
||||
Sabre_DAV_Auth_Plugin, and moved to the Sabre_DAVACL_Plugin.
|
||||
* Added: VObject library, for easy vcard/icalendar parsing using a natural
|
||||
interface.
|
||||
* Added: Ability to automatically generate full .ics feeds off calendars.
|
||||
To use: Add the Sabre_CalDAV_ICSExportPlugin, and add ?export to your
|
||||
calendar url.
|
||||
* Added: Plugins can now specify a pluginname, for easy access using
|
||||
Sabre_DAV_Server::getPlugin().
|
||||
* Added: beforeGetProperties event.
|
||||
* Added: updateProperties event.
|
||||
* Added: Principal listings and calendar-access can now be done privately,
|
||||
disallowing users from accessing or modifying other users' data.
|
||||
* Added: You can now pass arrays to the Sabre_DAV_Server constructor. If
|
||||
it's an array with node-objects, a Root collection will automatically be
|
||||
created, and the nodes are used as top-level children.
|
||||
* Added: The principal base uri is now customizable. It used to be
|
||||
hardcoded to 'principals/[user]'.
|
||||
* Added: getSupportedReportSet method in ServerPlugin class. This allows
|
||||
you to easily specify which reports you're implementing.
|
||||
* Added: A '..' link to the HTML browser.
|
||||
* Fixed: Issue 99: Locks on child elements were ignored when their parent
|
||||
nodes were deleted.
|
||||
* Fixed: Issue 90: lockdiscovery property and LOCK response now include a
|
||||
{DAV}lockroot element.
|
||||
* Fixed: Issue 96: support for 'default' collation in CalDAV text-match
|
||||
filters.
|
||||
* Fixed: Issue 102: Ensuring that copy and move with identical source and
|
||||
destination uri's fails.
|
||||
* Fixed: Issue 105: Supporting MKCALENDAR with no body.
|
||||
* Fixed: Issue 109: Small fixes in Sabre_HTTP_Util.
|
||||
* Fixed: Issue 111: Properly catching the ownername in a lock (if it's a
|
||||
string)
|
||||
* Fixed: Sabre_DAV_ObjectTree::nodeExist always returned false for the
|
||||
root node.
|
||||
* Added: Global way to easily supply new resourcetypes for certain node
|
||||
classes.
|
||||
* Fixed: Issue 59: Allowing the user to override the authentication realm
|
||||
in Sabre_CalDAV_Server.
|
||||
* Update: Issue 97: Looser time-range checking if there's a recurrence
|
||||
rule in an event. This fixes 'missing recurring events'.
|
||||
|
||||
1.3.0 (2010-10-14)
|
||||
* Added: childExists method to Sabre_DAV_ICollection. This is an api
|
||||
break, so if you implement Sabre_DAV_ICollection directly, add the method.
|
||||
* Changed: Almost all HTTP method implementations now take a uri argument,
|
||||
including events. This allows for internal rerouting of certain calls.
|
||||
If you have custom plugins, make sure they use this argument. If they
|
||||
don't, they will likely still work, but it might get in the way of
|
||||
future changes.
|
||||
* Changed: All getETag methods MUST now surround the etag with
|
||||
double-quotes. This was a mistake made in all previous SabreDAV
|
||||
versions. If you don't do this, any If-Match, If-None-Match and If:
|
||||
headers using Etags will work incorrectly. (Issue 85).
|
||||
* Added: Sabre_DAV_Auth_Backend_AbstractBasic class, which can be used to
|
||||
easily implement basic authentication.
|
||||
* Removed: Sabre_DAV_PermissionDenied class. Use Sabre_DAV_Forbidden
|
||||
instead.
|
||||
* Removed: Sabre_DAV_IDirectory interface, use Sabre_DAV_ICollection
|
||||
instead.
|
||||
* Added: Browser plugin now uses {DAV:}displayname if this property is
|
||||
available.
|
||||
* Added: Cache layer in the ObjectTree.
|
||||
* Added: Tree classes now have a delete and getChildren method.
|
||||
* Fixed: If-Modified-Since and If-Unmodified-Since would be incorrect if
|
||||
the date is an exact match.
|
||||
* Fixed: Support for multiple ETags in If-Match and If-None-Match headers.
|
||||
* Fixed: Improved baseUrl handling.
|
||||
* Fixed: Issue 67: Non-seekable stream support in ::put()/::get().
|
||||
* Fixed: Issue 65: Invalid dates are now ignored.
|
||||
* Updated: Refactoring in Sabre_CalDAV to make everything a bit more
|
||||
ledgable.
|
||||
* Fixed: Issue 88, Issue 89: Fixed compatibility for running SabreDAV on
|
||||
Windows.
|
||||
* Fixed: Issue 86: Fixed Content-Range top-boundary from 'file size' to
|
||||
'file size'-1.
|
||||
|
||||
1.2.4 (2010-07-13)
|
||||
* Fixed: Issue 62: Guessing baseUrl fails when url contains a
|
||||
query-string.
|
||||
* Added: Apache configuration sample for CGI/FastCGI setups.
|
||||
* Fixed: Issue 64: Only returning calendar-data when it was actually
|
||||
requested.
|
||||
|
||||
1.2.3 (2010-06-26)
|
||||
* Fixed: Issue 57: Supporting quotes around etags in If-Match and
|
||||
If-None-Match
|
||||
|
||||
1.2.2 (2010-06-21)
|
||||
* Updated: SabreDAV now attempts to guess the BaseURI if it's not set.
|
||||
* Updated: Better compatibility with BitKinex
|
||||
* Fixed: Issue 56: Incorrect behaviour for If-None-Match headers and GET
|
||||
requests.
|
||||
* Fixed: Issue with certain encoded paths in Browser Plugin.
|
||||
|
||||
1.2.1 (2010-06-07)
|
||||
* Fixed: Issue 50, patch by Mattijs Hoitink.
|
||||
* Fixed: Issue 51, Adding windows 7 lockfiles to TemporaryFileFilter.
|
||||
* Fixed: Issue 38, Allowing custom filters to be added to
|
||||
TemporaryFileFilter.
|
||||
* Fixed: Issue 53, ETags in the If: header were always failing. This
|
||||
behaviour is now corrected.
|
||||
* Added: Apache Authentication backend, in case authentication through
|
||||
.htaccess is desired.
|
||||
* Updated: Small improvements to example files.
|
||||
|
||||
1.2.0 (2010-05-24)
|
||||
* Fixed: Browser plugin now displays international characters.
|
||||
* Changed: More properties in CalDAV classes are now protected instead of
|
||||
private.
|
||||
|
||||
1.2.0beta3 (2010-05-14)
|
||||
* Fixed: Custom properties were not properly sent back for allprops
|
||||
requests.
|
||||
* Fixed: Issue 49, incorrect parsing of PROPPATCH, affecting Office 2007.
|
||||
* Changed: Removed CalDAV items from includes.php, and added a few missing
|
||||
ones.
|
||||
|
||||
1.2.0beta2 (2010-05-04)
|
||||
* Fixed: Issue 46: Fatal error for some non-existent nodes.
|
||||
* Updated: some example sql to include email address.
|
||||
* Added: 208 and 508 statuscodes from RFC5842.
|
||||
* Added: Apache2 configuration examples
|
||||
|
||||
1.2.0beta1 (2010-04-28)
|
||||
* Fixed: redundant namespace declaration in resourcetypes.
|
||||
* Fixed: 2 locking bugs triggered by litmus when no Sabre_DAV_ILockable
|
||||
interface is used.
|
||||
* Changed: using http://sabredav.org/ns for all custom xml properties.
|
||||
* Added: email address property to principals.
|
||||
* Updated: CalendarObject validation.
|
||||
|
||||
1.2.0alpha4 (2010-04-24)
|
||||
* Added: Support for If-Range, If-Match, If-None-Match, If-Modified-Since,
|
||||
If-Unmodified-Since.
|
||||
* Changed: Brand new build system. Functionality is split up between
|
||||
Sabre, Sabre_HTTP, Sabre_DAV and Sabre_CalDAV packages. In addition to
|
||||
that a new non-pear package will be created with all this functionality
|
||||
combined.
|
||||
* Changed: Autoloader moved to Sabre/autoload.php.
|
||||
* Changed: The Allow: header is now more accurate, with appropriate HTTP
|
||||
methods per uri.
|
||||
* Changed: Now throwing back Sabre_DAV_Exception_MethodNotAllowed on a few
|
||||
places where Sabre_DAV_Exception_NotImplemented was used.
|
||||
|
||||
1.2.0alpha3 (2010-04-20)
|
||||
* Update: Complete rewrite of property updating. Now easier to use and
|
||||
atomic.
|
||||
* Fixed: Issue 16, automatically adding trailing / to baseUri.
|
||||
* Added: text/plain is used for .txt files in GuessContentType plugin.
|
||||
* Added: support for principal-property-search and
|
||||
principal-search-property-set reports.
|
||||
* Added: Issue 31: Hiding exception information by default. Can be turned
|
||||
on with the Sabre_DAV_Server::$debugExceptions property.
|
||||
|
||||
1.2.0alpha2 (2010-04-08)
|
||||
* Added: Calendars are now private and can only be read by the owner.
|
||||
* Fixed: double namespace declaration in multistatus responses.
|
||||
* Added: MySQL database dumps. MySQL is now also supported next to SQLite.
|
||||
* Added: expand-properties REPORT from RFC 3253.
|
||||
* Added: Sabre_DAV_Property_IHref interface for properties exposing urls.
|
||||
* Added: Issue 25: Throwing error on broken Finder behaviour.
|
||||
* Changed: Authentication backend is now aware of current user.
|
||||
|
||||
1.2.0alpha1 (2010-03-31)
|
||||
* Fixed: Issue 26: Workaround for broken GVFS behaviour with encoded
|
||||
special characters.
|
||||
* Fixed: Issue 34: Incorrect Lock-Token response header for LOCK. Fixes
|
||||
Office 2010 compatibility.
|
||||
* Added: Issue 35: SabreDAV version to header to OPTIONS response to ease
|
||||
debugging.
|
||||
* Fixed: Issue 36: Incorrect variable name, throwing error in some
|
||||
requests.
|
||||
* Fixed: Issue 37: Incorrect smultron regex in temporary filefilter.
|
||||
* Fixed: Issue 33: Converting ISO-8859-1 characters to UTF-8.
|
||||
* Fixed: Issue 39 & Issue 40: Basename fails on non-utf-8 locales.
|
||||
* Added: More unittests.
|
||||
* Added: SabreDAV version to all error responses.
|
||||
* Added: URLUtil class for decoding urls.
|
||||
* Changed: Now using pear.sabredav.org pear channel.
|
||||
* Changed: Sabre_DAV_Server::getCopyAndMoveInfo is now a public method.
|
||||
|
||||
1.1.2-alpha (2010-03-18)
|
||||
* Added: RFC5397 - current-user-principal support.
|
||||
* Fixed: Issue 27: encoding entities in property responses.
|
||||
* Added: naturalselection script now allows the user to specify a 'minimum
|
||||
number of bytes' for deletion. This should reduce load due to less
|
||||
crawling
|
||||
* Added: Full support for the calendar-query report.
|
||||
* Added: More unittests.
|
||||
* Added: Support for complex property deserialization through the static
|
||||
::unserialize() method.
|
||||
* Added: Support for modifying calendar-component-set
|
||||
* Fixed: Issue 29: Added TIMEOUT_INFINITE constant
|
||||
|
||||
1.1.1-alpha (2010-03-11)
|
||||
* Added: RFC5689 - Extended MKCOL support.
|
||||
* Fixed: Evolution support for CalDAV.
|
||||
* Fixed: PDO-locks backend was pretty much completely broken. This is
|
||||
100% unittested now.
|
||||
* Added: support for ctags.
|
||||
* Fixed: Comma's between HTTP methods in 'Allow' method.
|
||||
* Changed: default argument for Sabre_DAV_Locks_Backend_FS. This means a
|
||||
datadirectory must always be specified from now on.
|
||||
* Changed: Moved Sabre_DAV_Server::parseProps to
|
||||
Sabre_DAV_XMLUtil::parseProperties.
|
||||
* Changed: Sabre_DAV_IDirectory is now Sabre_DAV_ICollection.
|
||||
* Changed: Sabre_DAV_Exception_PermissionDenied is now
|
||||
Sabre_DAV_Exception_Forbidden.
|
||||
* Changed: Sabre_CalDAV_ICalendarCollection is removed.
|
||||
* Added: Sabre_DAV_IExtendedCollection.
|
||||
* Added: Many more unittests.
|
||||
* Added: support for calendar-timezone property.
|
||||
|
||||
1.1.0-alpha (2010-03-01)
|
||||
* Note: This version is forked from version 1.0.5, so release dates may be
|
||||
out of order.
|
||||
* Added: CalDAV - RFC 4791
|
||||
* Removed: Sabre_PHP_Exception. PHP has a built-in ErrorException for
|
||||
this.
|
||||
* Added: PDO authentication backend.
|
||||
* Added: Example sql for auth, caldav, locks for sqlite.
|
||||
* Added: Sabre_DAV_Browser_GuessContentType plugin
|
||||
* Changed: Authentication plugin refactored, making it possible to
|
||||
implement non-digest authentication.
|
||||
* Fixed: Better error display in browser plugin.
|
||||
* Added: Support for {DAV:}supported-report-set
|
||||
* Added: XML utility class with helper functions for the WebDAV protocol.
|
||||
* Added: Tons of unittests
|
||||
* Added: PrincipalCollection and Principal classes
|
||||
* Added: Sabre_DAV_Server::getProperties for easy property retrieval
|
||||
* Changed: {DAV:}resourceType defaults to 0
|
||||
* Changed: Any non-null resourceType now gets a / appended to the href
|
||||
value. Before this was just for {DAV:}collection's, but this is now also
|
||||
the case for for example {DAV:}principal.
|
||||
* Changed: The Href property class can now optionally create non-relative
|
||||
uri's.
|
||||
* Changed: Sabre_HTTP_Response now returns false if headers are already
|
||||
sent and header-methods are called.
|
||||
* Fixed: Issue 19: HEAD requests on Collections
|
||||
* Fixed: Issue 21: Typo in Sabre_DAV_Property_Response
|
||||
* Fixed: Issue 18: Doesn't work with Evolution Contacts
|
||||
|
||||
1.0.15-stable (2010-05-28)
|
||||
* Added: Issue 31: Hiding exception information by default. Can be turned
|
||||
on with the Sabre_DAV_Server::$debugExceptions property.
|
||||
* Added: Moved autoload from lib/ to lib/Sabre/autoload.php. This is also
|
||||
the case in the upcoming 1.2.0, so it will improve future compatibility.
|
||||
|
||||
1.0.14-stable (2010-04-15)
|
||||
* Fixed: double namespace declaration in multistatus responses.
|
||||
|
||||
1.0.13-stable (2010-03-30)
|
||||
* Fixed: Issue 40: Last references to basename/dirname
|
||||
|
||||
1.0.12-stable (2010-03-30)
|
||||
* Fixed: Issue 37: Incorrect smultron regex in temporary filefilter.
|
||||
* Fixed: Issue 26: Workaround for broken GVFS behaviour with encoded
|
||||
special characters.
|
||||
* Fixed: Issue 33: Converting ISO-8859-1 characters to UTF-8.
|
||||
* Fixed: Issue 39: Basename fails on non-utf-8 locales.
|
||||
* Added: More unittests.
|
||||
* Added: SabreDAV version to all error responses.
|
||||
* Added: URLUtil class for decoding urls.
|
||||
* Updated: Now using pear.sabredav.org pear channel.
|
||||
|
||||
1.0.11-stable (2010-03-23)
|
||||
* Non-public release. This release is identical to 1.0.10, but it is used
|
||||
to test releasing packages to pear.sabredav.org.
|
||||
|
||||
1.0.10-stable (2010-03-22)
|
||||
* Fixed: Issue 34: Invalid Lock-Token header response.
|
||||
* Added: Issue 35: Addign SabreDAV version to HTTP OPTIONS responses.
|
||||
|
||||
1.0.9-stable (2010-03-19)
|
||||
* Fixed: Issue 27: Entities not being encoded in PROPFIND responses.
|
||||
* Fixed: Issue 29: Added missing TIMEOUT_INFINITE constant.
|
||||
|
||||
1.0.8-stable (2010-03-03)
|
||||
* Fixed: Issue 21: typos causing errors
|
||||
* Fixed: Issue 23: Comma's between methods in Allow header.
|
||||
* Added: Sabre_DAV_ICollection interface, to aid in future compatibility.
|
||||
* Added: Sabre_DAV_Exception_Forbidden exception. This will replace
|
||||
Sabre_DAV_Exception_PermissionDenied in the future, and can already be
|
||||
used to ensure future compatibility.
|
||||
|
||||
1.0.7-stable (2010-02-24)
|
||||
* Fixed: Issue 19 regression for MS Office
|
||||
|
||||
1.0.6-stable (2010-02-23)
|
||||
* Fixed: Issue 19: HEAD requests on Collections
|
||||
|
||||
1.0.5-stable (2010-01-22)
|
||||
* Fixed: Fatal error when a malformed url was used for unlocking, in
|
||||
conjuction with Sabre.autoload.php due to a incorrect filename.
|
||||
* Fixed: Improved unittests and build system
|
||||
|
||||
1.0.4-stable (2010-01-11)
|
||||
* Fixed: needed 2 different releases. One for googlecode and one for
|
||||
pearfarm. This is to retain the old method to install SabreDAV until
|
||||
pearfarm becomes the standard installation method.
|
||||
|
||||
1.0.3-stable (2010-01-11)
|
||||
* Added: RFC4709 support (davmount)
|
||||
* Added: 6 unittests
|
||||
* Added: naturalselection. A tool to keep cache directories below a
|
||||
specified theshold.
|
||||
* Changed: Now using pearfarm.org channel server.
|
||||
|
||||
1.0.1-stable (2009-12-22)
|
||||
* Fixed: Issue 15: typos in examples
|
||||
* Fixed: Minor pear installation issues
|
||||
|
||||
1.0.0-stable (2009-11-02)
|
||||
* Added: SimpleDirectory class. This class allows creating static
|
||||
directory structures with ease.
|
||||
* Changed: Custom complex properties and exceptions now get an instance of
|
||||
Sabre_DAV_Server as their first argument in serialize()
|
||||
* Changed: Href complex property now prepends server's baseUri
|
||||
* Changed: delete before an overwriting copy/move is now handles by server
|
||||
class instead of tree classes
|
||||
* Changed: events must now explicitly return false to stop execution.
|
||||
Before, execution would be stopped by anything loosely evaluating to
|
||||
false.
|
||||
* Changed: the getPropertiesForPath method now takes a different set of
|
||||
arguments, and returns a different response. This allows plugin
|
||||
developers to return statuses for properties other than 200 and 404. The
|
||||
hrefs are now also always calculated relative to the baseUri, and not
|
||||
the uri of the request.
|
||||
* Changed: generatePropFindResponse is renamed to generateMultiStatus, and
|
||||
now takes a list of properties similar to the response of
|
||||
getPropertiesForPath. This was also needed to improve flexibility for
|
||||
plugin development.
|
||||
* Changed: Auth plugins are no longer included. They were not yet stable
|
||||
quality, so they will probably be reintroduced in a later version.
|
||||
* Changed: PROPPATCH also used generateMultiStatus now.
|
||||
* Removed: unknownProperties event. This is replaced by the
|
||||
afterGetProperties event, which should provide more flexibility.
|
||||
* Fixed: Only calling getSize() on IFile instances in httpHead()
|
||||
* Added: beforeBind event. This is invoked upon file or directory creation
|
||||
* Added: beforeWriteContent event, this is invoked by PUT and LOCK on an
|
||||
existing resource.
|
||||
* Added: beforeUnbind event. This is invoked right before deletion of any
|
||||
resource.
|
||||
* Added: afterGetProperties event. This event can be used to make
|
||||
modifications to property responses.
|
||||
* Added: beforeLock and beforeUnlock events.
|
||||
* Added: afterBind event.
|
||||
* Fixed: Copy and Move could fail in the root directory. This is now
|
||||
fixed.
|
||||
* Added: Plugins can now be retrieved by their classname. This is useful
|
||||
for inter-plugin communication.
|
||||
* Added: The Auth backend can now return usernames and user-id's.
|
||||
* Added: The Auth backend got a getUsers method
|
||||
* Added: Sabre_DAV_FSExt_Directory now returns quota info
|
||||
|
||||
0.12.1-beta (2009-09-11)
|
||||
* Fixed: UNLOCK bug. Unlock didn't work at all
|
||||
|
||||
0.12-beta (2009-09-10)
|
||||
* Updated: Browser plugin now shows multiple {DAV:}resourcetype values
|
||||
if available.
|
||||
* Added: Experimental PDO backend for Locks Manager
|
||||
* Fixed: Sending Content-Length: 0 for every empty response. This
|
||||
improves NGinx compatibility.
|
||||
* Fixed: Last modification time is reported in UTC timezone. This improves
|
||||
Finder compatibility.
|
||||
|
||||
0.11-beta (2009-08-11)
|
||||
* Updated: Now in Beta
|
||||
* Updated: Pear package no longer includes docs/ directory. These just
|
||||
contained rfc's, which are publically available. This reduces the
|
||||
package from ~800k to ~60k
|
||||
* Added: generatePropfindResponse now takes a baseUri argument
|
||||
* Added: ResourceType property can now contain multiple resourcetypes.
|
||||
* Fixed: Issue 13.
|
||||
|
||||
0.10-alpha (2009-08-03)
|
||||
* Added: Plugin to automatically map GET requests to non-files to
|
||||
PROPFIND (Sabre_DAV_Browser_MapGetToPropFind). This should allow
|
||||
easier debugging of complicated WebDAV setups.
|
||||
* Added: Sabre_DAV_Property_Href class. For future use.
|
||||
* Added: Ability to choose to use auth-int, auth or both for HTTP Digest
|
||||
authentication. (Issue 11)
|
||||
* Changed: Made more methods in Sabre_DAV_Server public.
|
||||
* Fixed: TemporaryFileFilter plugin now intercepts HTTP LOCK requests
|
||||
to non-existent files. (Issue 12)
|
||||
* Added: Central list of defined xml namespace prefixes. This can reduce
|
||||
Bandwidth and legibility for xml bodies with user-defined namespaces.
|
||||
* Added: now a PEAR-compatible package again, thanks to Michael Gauthier
|
||||
* Changed: moved default copy and move logic from ObjectTree to Tree class
|
||||
|
||||
0.9-alpha (2009-07-21)
|
||||
* Changed: Major refactoring, removed most of the logic from the Tree
|
||||
objects. The Server class now directly works with the INode, IFile
|
||||
and IDirectory objects. If you created your own Tree objects,
|
||||
this will most likely break in this release.
|
||||
* Changed: Moved all the Locking logic from the Tree and Server classes
|
||||
into a separate plugin.
|
||||
* Changed: TemporaryFileFilter is now a plugin.
|
||||
* Added: Comes with an autoloader script. This can be used instead of
|
||||
the includer script, and is preferred by some people.
|
||||
* Added: AWS Authentication class.
|
||||
* Added: simpleserversetup.py script. This will quickly get a fileserver
|
||||
up and running.
|
||||
* Added: When subscribing to events, it is now possible to supply a
|
||||
priority. This is for example needed to ensure that the Authentication
|
||||
Plugin is used before any other Plugin.
|
||||
* Added: 22 new tests.
|
||||
* Added: Users-manager plugin for .htdigest files. Experimental and
|
||||
subject to change.
|
||||
* Added: RFC 2324 HTTP 418 status code
|
||||
* Fixed: Exclusive locks could in some cases be picked up as shared locks
|
||||
* Fixed: Digest auth for non-apache servers had a bug (still not actually
|
||||
tested this well).
|
||||
|
||||
0.8-alpha (2009-05-30)
|
||||
* Changed: Renamed all exceptions! This is a compatibility break. Every
|
||||
Exception now follows Sabre_DAV_Exception_FileNotFound convention
|
||||
instead of Sabre_DAV_FileNotFoundException.
|
||||
* Added: Browser plugin now allows uploading and creating directories
|
||||
straight from the browser.
|
||||
* Added: 12 more unittests
|
||||
* Fixed: Locking bug, which became prevalent on Windows Vista.
|
||||
* Fixed: Netdrive support
|
||||
* Fixed: TemporaryFileFilter filtered out too many files. Fixed some
|
||||
of the regexes.
|
||||
* Fixed: Added README and ChangeLog to package
|
||||
|
||||
0.7-alpha (2009-03-29)
|
||||
* Added: System to return complex properties from PROPFIND.
|
||||
* Added: support for {DAV:}supportedlock.
|
||||
* Added: support for {DAV:}lockdiscovery.
|
||||
* Added: 6 new tests.
|
||||
* Added: New plugin system.
|
||||
* Added: Simple HTML directory plugin, for browser access.
|
||||
* Added: Server class now sends back standard pre-condition error xml
|
||||
bodies. This was new since RFC4918.
|
||||
* Added: Sabre_DAV_Tree_Aggregrate, which can 'host' multiple Tree objects
|
||||
into one.
|
||||
* Added: simple basis for HTTP REPORT method. This method is not used yet,
|
||||
but can be used by plugins to add reports.
|
||||
* Changed: ->getSize is only called for files, no longer for collections.
|
||||
r303
|
||||
* Changed: Sabre_DAV_FilterTree is now Sabre_DAV_Tree_Filter
|
||||
* Changed: Sabre_DAV_TemporaryFileFilter is now called
|
||||
Sabre_DAV_Tree_TemporaryFileFilter.
|
||||
* Changed: removed functions (get(/set)HTTPRequest(/Response)) from Server
|
||||
class, and using a public property instead.
|
||||
* Fixed: bug related to parsing proppatch and propfind requests. Didn't
|
||||
show up in most clients, but it needed fixing regardless. (r255)
|
||||
* Fixed: auth-int is now properly supported within HTTP Digest.
|
||||
* Fixed: Using application/xml for a mimetype vs. text/xml as per RFC4918
|
||||
sec 8.2.
|
||||
* Fixed: TemporaryFileFilter now lets through GET's if they actually
|
||||
exist on the backend. (r274)
|
||||
* FIxed: Some methods didn't get passed through in the FilterTree (r283).
|
||||
* Fixed: LockManager is now slightly more complex, Tree classes slightly
|
||||
less. (r287)
|
||||
|
||||
0.6-alpha (2009-02-16)
|
||||
* Added: Now uses streams for files, instead of strings.
|
||||
This means it won't require to hold entire files in memory, which can be
|
||||
an issue if you're dealing with big files. Note that this breaks
|
||||
compatibility for put() and createFile methods.
|
||||
* Added: HTTP Digest Authentication helper class.
|
||||
* Added: Support for HTTP Range header
|
||||
* Added: Support for ETags within If: headers
|
||||
* Added: The API can now return ETags and override the default Content-Type
|
||||
* Added: starting with basic framework for unittesting, using PHPUnit.
|
||||
* Added: 49 unittests.
|
||||
* Added: Abstraction for the HTTP request.
|
||||
* Updated: Using Clark Notation for tags in properties. This means tags
|
||||
are serialized as {namespace}tagName instead of namespace#tagName
|
||||
* Fixed: HTTP_BasicAuth class now works as expected.
|
||||
* Fixed: DAV_Server uses / for a default baseUrl.
|
||||
* Fixed: Last modification date is no longer ignored in PROPFIND.
|
||||
* Fixed: PROPFIND now sends back information about the requestUri even
|
||||
when "Depth: 1" is specified.
|
||||
|
||||
0.5-alpha (2009-01-14)
|
||||
* Added: Added a very simple example for implementing a mapping to PHP
|
||||
file streams. This should allow easy implementation of for example a
|
||||
WebDAV to FTP proxy.
|
||||
* Added: HTTP Basic Authentication helper class.
|
||||
* Added: Sabre_HTTP_Response class. This centralizes HTTP operations and
|
||||
will be a start towards the creating of a testing framework.
|
||||
* Updated: Backwards compatibility break: all require_once() statements
|
||||
are removed
|
||||
from all the files. It is now recommended to use autoloading of
|
||||
classes, or just including lib/Sabre.includes.php. This fix was made
|
||||
to allow easier integration into applications not using this standard
|
||||
inclusion model.
|
||||
* Updated: Better in-file documentation.
|
||||
* Updated: Sabre_DAV_Tree can now work with Sabre_DAV_LockManager.
|
||||
* Updated: Fixes a shared-lock bug.
|
||||
* Updated: Removed ?> from the bottom of each php file.
|
||||
* Updated: Split up some operations from Sabre_DAV_Server to
|
||||
Sabre_HTTP_Response.
|
||||
* Fixed: examples are now actually included in the pear package.
|
||||
|
||||
0.4-alpha (2008-11-05)
|
||||
* Passes all litmus tests!
|
||||
* Added: more examples
|
||||
* Added: Custom property support
|
||||
* Added: Shared lock support
|
||||
* Added: Depth support to locks
|
||||
* Added: Locking on unmapped urls (non-existent nodes)
|
||||
* Fixed: Advertising as WebDAV class 3 support
|
||||
|
||||
0.3-alpha (2008-06-29)
|
||||
* Fully working in MS Windows clients.
|
||||
* Added: temporary file filter: support for smultron files.
|
||||
* Added: Phing build scripts
|
||||
* Added: PEAR package
|
||||
* Fixed: MOVE bug identified using finder.
|
||||
* Fixed: Using gzuncompress instead of gzdecode in the temporary file
|
||||
filter. This seems more common.
|
||||
|
||||
0.2-alpha (2008-05-27)
|
||||
* Somewhat working in Windows clients
|
||||
* Added: Working PROPPATCH method (doesn't support custom properties yet)
|
||||
* Added: Temporary filename handling system
|
||||
* Added: Sabre_DAV_IQuota to return quota information
|
||||
* Added: PROPFIND now reads the request body and only supplies the
|
||||
requested properties
|
||||
|
||||
0.1-alpha (2008-04-04)
|
||||
* First release!
|
||||
* Passes litmus: basic, http and copymove test.
|
||||
* Fully working in Finder and DavFSv2
|
||||
|
||||
Project started: 2007-12-13
|
28
dav/SabreDAV/LICENSE
Normal file
28
dav/SabreDAV/LICENSE
Normal file
|
@ -0,0 +1,28 @@
|
|||
Copyright (C) 2007-2012 Rooftop Solutions.
|
||||
Copyright (C) 2007-2009 FileMobile inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of the SabreDAV nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
248
dav/SabreDAV/bin/googlecode_upload.py
Executable file
248
dav/SabreDAV/bin/googlecode_upload.py
Executable file
|
@ -0,0 +1,248 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2006, 2007 Google Inc. All Rights Reserved.
|
||||
# Author: danderson@google.com (David Anderson)
|
||||
#
|
||||
# Script for uploading files to a Google Code project.
|
||||
#
|
||||
# This is intended to be both a useful script for people who want to
|
||||
# streamline project uploads and a reference implementation for
|
||||
# uploading files to Google Code projects.
|
||||
#
|
||||
# To upload a file to Google Code, you need to provide a path to the
|
||||
# file on your local machine, a small summary of what the file is, a
|
||||
# project name, and a valid account that is a member or owner of that
|
||||
# project. You can optionally provide a list of labels that apply to
|
||||
# the file. The file will be uploaded under the same name that it has
|
||||
# in your local filesystem (that is, the "basename" or last path
|
||||
# component). Run the script with '--help' to get the exact syntax
|
||||
# and available options.
|
||||
#
|
||||
# Note that the upload script requests that you enter your
|
||||
# googlecode.com password. This is NOT your Gmail account password!
|
||||
# This is the password you use on googlecode.com for committing to
|
||||
# Subversion and uploading files. You can find your password by going
|
||||
# to http://code.google.com/hosting/settings when logged in with your
|
||||
# Gmail account. If you have already committed to your project's
|
||||
# Subversion repository, the script will automatically retrieve your
|
||||
# credentials from there (unless disabled, see the output of '--help'
|
||||
# for details).
|
||||
#
|
||||
# If you are looking at this script as a reference for implementing
|
||||
# your own Google Code file uploader, then you should take a look at
|
||||
# the upload() function, which is the meat of the uploader. You
|
||||
# basically need to build a multipart/form-data POST request with the
|
||||
# right fields and send it to https://PROJECT.googlecode.com/files .
|
||||
# Authenticate the request using HTTP Basic authentication, as is
|
||||
# shown below.
|
||||
#
|
||||
# Licensed under the terms of the Apache Software License 2.0:
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Questions, comments, feature requests and patches are most welcome.
|
||||
# Please direct all of these to the Google Code users group:
|
||||
# http://groups.google.com/group/google-code-hosting
|
||||
|
||||
"""Google Code file uploader script.
|
||||
"""
|
||||
|
||||
__author__ = 'danderson@google.com (David Anderson)'
|
||||
|
||||
import httplib
|
||||
import os.path
|
||||
import optparse
|
||||
import getpass
|
||||
import base64
|
||||
import sys
|
||||
|
||||
|
||||
def upload(file, project_name, user_name, password, summary, labels=None):
|
||||
"""Upload a file to a Google Code project's file server.
|
||||
|
||||
Args:
|
||||
file: The local path to the file.
|
||||
project_name: The name of your project on Google Code.
|
||||
user_name: Your Google account name.
|
||||
password: The googlecode.com password for your account.
|
||||
Note that this is NOT your global Google Account password!
|
||||
summary: A small description for the file.
|
||||
labels: an optional list of label strings with which to tag the file.
|
||||
|
||||
Returns: a tuple:
|
||||
http_status: 201 if the upload succeeded, something else if an
|
||||
error occurred.
|
||||
http_reason: The human-readable string associated with http_status
|
||||
file_url: If the upload succeeded, the URL of the file on Google
|
||||
Code, None otherwise.
|
||||
"""
|
||||
# The login is the user part of user@gmail.com. If the login provided
|
||||
# is in the full user@domain form, strip it down.
|
||||
if user_name.endswith('@gmail.com'):
|
||||
user_name = user_name[:user_name.index('@gmail.com')]
|
||||
|
||||
form_fields = [('summary', summary)]
|
||||
if labels is not None:
|
||||
form_fields.extend([('label', l.strip()) for l in labels])
|
||||
|
||||
content_type, body = encode_upload_request(form_fields, file)
|
||||
|
||||
upload_host = '%s.googlecode.com' % project_name
|
||||
upload_uri = '/files'
|
||||
auth_token = base64.b64encode('%s:%s'% (user_name, password))
|
||||
headers = {
|
||||
'Authorization': 'Basic %s' % auth_token,
|
||||
'User-Agent': 'Googlecode.com uploader v0.9.4',
|
||||
'Content-Type': content_type,
|
||||
}
|
||||
|
||||
server = httplib.HTTPSConnection(upload_host)
|
||||
server.request('POST', upload_uri, body, headers)
|
||||
resp = server.getresponse()
|
||||
server.close()
|
||||
|
||||
if resp.status == 201:
|
||||
location = resp.getheader('Location', None)
|
||||
else:
|
||||
location = None
|
||||
return resp.status, resp.reason, location
|
||||
|
||||
|
||||
def encode_upload_request(fields, file_path):
|
||||
"""Encode the given fields and file into a multipart form body.
|
||||
|
||||
fields is a sequence of (name, value) pairs. file is the path of
|
||||
the file to upload. The file will be uploaded to Google Code with
|
||||
the same file name.
|
||||
|
||||
Returns: (content_type, body) ready for httplib.HTTP instance
|
||||
"""
|
||||
BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla'
|
||||
CRLF = '\r\n'
|
||||
|
||||
body = []
|
||||
|
||||
# Add the metadata about the upload first
|
||||
for key, value in fields:
|
||||
body.extend(
|
||||
['--' + BOUNDARY,
|
||||
'Content-Disposition: form-data; name="%s"' % key,
|
||||
'',
|
||||
value,
|
||||
])
|
||||
|
||||
# Now add the file itself
|
||||
file_name = os.path.basename(file_path)
|
||||
f = open(file_path, 'rb')
|
||||
file_content = f.read()
|
||||
f.close()
|
||||
|
||||
body.extend(
|
||||
['--' + BOUNDARY,
|
||||
'Content-Disposition: form-data; name="filename"; filename="%s"'
|
||||
% file_name,
|
||||
# The upload server determines the mime-type, no need to set it.
|
||||
'Content-Type: application/octet-stream',
|
||||
'',
|
||||
file_content,
|
||||
])
|
||||
|
||||
# Finalize the form body
|
||||
body.extend(['--' + BOUNDARY + '--', ''])
|
||||
|
||||
return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body)
|
||||
|
||||
|
||||
def upload_find_auth(file_path, project_name, summary, labels=None,
|
||||
user_name=None, password=None, tries=3):
|
||||
"""Find credentials and upload a file to a Google Code project's file server.
|
||||
|
||||
file_path, project_name, summary, and labels are passed as-is to upload.
|
||||
|
||||
Args:
|
||||
file_path: The local path to the file.
|
||||
project_name: The name of your project on Google Code.
|
||||
summary: A small description for the file.
|
||||
labels: an optional list of label strings with which to tag the file.
|
||||
config_dir: Path to Subversion configuration directory, 'none', or None.
|
||||
user_name: Your Google account name.
|
||||
tries: How many attempts to make.
|
||||
"""
|
||||
|
||||
while tries > 0:
|
||||
if user_name is None:
|
||||
# Read username if not specified or loaded from svn config, or on
|
||||
# subsequent tries.
|
||||
sys.stdout.write('Please enter your googlecode.com username: ')
|
||||
sys.stdout.flush()
|
||||
user_name = sys.stdin.readline().rstrip()
|
||||
if password is None:
|
||||
# Read password if not loaded from svn config, or on subsequent tries.
|
||||
print 'Please enter your googlecode.com password.'
|
||||
print '** Note that this is NOT your Gmail account password! **'
|
||||
print 'It is the password you use to access Subversion repositories,'
|
||||
print 'and can be found here: http://code.google.com/hosting/settings'
|
||||
password = getpass.getpass()
|
||||
|
||||
status, reason, url = upload(file_path, project_name, user_name, password,
|
||||
summary, labels)
|
||||
# Returns 403 Forbidden instead of 401 Unauthorized for bad
|
||||
# credentials as of 2007-07-17.
|
||||
if status in [httplib.FORBIDDEN, httplib.UNAUTHORIZED]:
|
||||
# Rest for another try.
|
||||
user_name = password = None
|
||||
tries = tries - 1
|
||||
else:
|
||||
# We're done.
|
||||
break
|
||||
|
||||
return status, reason, url
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY '
|
||||
'-p PROJECT [options] FILE')
|
||||
parser.add_option('-s', '--summary', dest='summary',
|
||||
help='Short description of the file')
|
||||
parser.add_option('-p', '--project', dest='project',
|
||||
help='Google Code project name')
|
||||
parser.add_option('-u', '--user', dest='user',
|
||||
help='Your Google Code username')
|
||||
parser.add_option('-w', '--password', dest='password',
|
||||
help='Your Google Code password')
|
||||
parser.add_option('-l', '--labels', dest='labels',
|
||||
help='An optional list of comma-separated labels to attach '
|
||||
'to the file')
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if not options.summary:
|
||||
parser.error('File summary is missing.')
|
||||
elif not options.project:
|
||||
parser.error('Project name is missing.')
|
||||
elif len(args) < 1:
|
||||
parser.error('File to upload not provided.')
|
||||
elif len(args) > 1:
|
||||
parser.error('Only one file may be specified.')
|
||||
|
||||
file_path = args[0]
|
||||
|
||||
if options.labels:
|
||||
labels = options.labels.split(',')
|
||||
else:
|
||||
labels = None
|
||||
|
||||
status, reason, url = upload_find_auth(file_path, options.project,
|
||||
options.summary, labels,
|
||||
options.user, options.password)
|
||||
if url:
|
||||
print 'The file was uploaded successfully.'
|
||||
print 'URL: %s' % url
|
||||
return 0
|
||||
else:
|
||||
print 'An error occurred. Your file was not uploaded.'
|
||||
print 'Google Code upload server said: %s (%s)' % (reason, status)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
378
dav/SabreDAV/bin/gwdg.php
Executable file
378
dav/SabreDAV/bin/gwdg.php
Executable file
|
@ -0,0 +1,378 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Documentation generator
|
||||
*
|
||||
* This scripts scans all files in the lib/ directory, and generates
|
||||
* Google Code wiki documentation.
|
||||
*
|
||||
* This script is rather crappy. It does what it needs to do, but uses global
|
||||
* variables and it might be a hard to read.
|
||||
*
|
||||
* I'm not sure if I care though. Maybe one day this can become a separate
|
||||
* project
|
||||
*
|
||||
* To run this script, just execute on the command line. The script assumes
|
||||
* it's in the standard bin/ directory.
|
||||
*/
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
$libDir = realpath(__DIR__ . '/../lib');
|
||||
$outputDir = __DIR__ . '/../docs/wikidocs';
|
||||
|
||||
if (!is_dir($outputDir)) mkdir($outputDir);
|
||||
|
||||
$files = new RecursiveDirectoryIterator($libDir);
|
||||
$files = new RecursiveIteratorIterator($files, RecursiveIteratorIterator::LEAVES_ONLY);
|
||||
|
||||
include_once $libDir . '/Sabre/autoload.php';
|
||||
|
||||
// Finding all classnames
|
||||
$classNames = findClassNames($files);
|
||||
echo "Found: " . count($classNames) . " classes and interfaces\n";
|
||||
|
||||
echo "Generating class tree\n";
|
||||
$classTree = getClassTree($classNames);
|
||||
|
||||
$packageList = array();
|
||||
|
||||
foreach($classNames as $className) {
|
||||
|
||||
echo "Creating docs for: " . $className . "\n";
|
||||
|
||||
$output = createDoc($className,isset($classTree[$className])?$classTree[$className]:array());
|
||||
file_put_contents($outputDir . '/' . $className . '.wiki', $output);
|
||||
|
||||
}
|
||||
|
||||
echo "Creating indexes\n";
|
||||
$output = createSidebarIndex($packageList);
|
||||
file_put_contents($outputDir . '/APIIndex.wiki', $output);
|
||||
|
||||
|
||||
function findClassNames($files) {
|
||||
|
||||
$classNames = array();
|
||||
foreach($files as $fileName=>$fileInfo) {
|
||||
|
||||
$tokens = token_get_all(file_get_contents($fileName));
|
||||
foreach($tokens as $tokenIndex=>$token) {
|
||||
|
||||
if ($token[0]===T_CLASS || $token[0]===T_INTERFACE) {
|
||||
$classNames[] = $tokens[$tokenIndex+2][1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $classNames;
|
||||
|
||||
}
|
||||
|
||||
function getClassTree($classNames) {
|
||||
|
||||
$classTree = array();
|
||||
|
||||
foreach($classNames as $className) {
|
||||
|
||||
if (!class_exists($className) && !interface_exists($className)) continue;
|
||||
$rClass = new ReflectionClass($className);
|
||||
|
||||
$parent = $rClass->getParentClass();
|
||||
if ($parent) $parent = $parent->name;
|
||||
|
||||
if (!isset($classTree[$parent])) $classTree[$parent] = array();
|
||||
$classTree[$parent][] = $className;
|
||||
|
||||
foreach($rClass->getInterfaceNames() as $interface) {
|
||||
|
||||
if (!isset($classTree[$interface])) {
|
||||
$classTree[$interface] = array();
|
||||
}
|
||||
$classTree[$interface][] = $className;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return $classTree;
|
||||
|
||||
}
|
||||
|
||||
function createDoc($className, $extendedBy) {
|
||||
|
||||
// ew
|
||||
global $packageList;
|
||||
|
||||
ob_start();
|
||||
$rClass = new ReflectionClass($className);
|
||||
|
||||
echo "#summary API documentation for: ", $rClass->getName() , "\n";
|
||||
echo "#labels APIDoc\n";
|
||||
echo "#sidebar APIIndex\n";
|
||||
echo "=`" . $rClass->getName() . "`=\n";
|
||||
echo "\n";
|
||||
|
||||
$docs = parseDocs($rClass->getDocComment());
|
||||
echo $docs['description'] . "\n";
|
||||
echo "\n";
|
||||
|
||||
$parentClass = $rClass->getParentClass();
|
||||
|
||||
if($parentClass) {
|
||||
echo " * Parent class: [" . $parentClass->getName() . "]\n";
|
||||
}
|
||||
if ($interfaces = $rClass->getInterfaceNames()) {
|
||||
$interfaces = array_map(function($int) { return '[' . $int . ']'; },$interfaces);
|
||||
echo " * Implements: " . implode(", ", $interfaces) . "\n";
|
||||
}
|
||||
$classType = $rClass->isInterface()?'interface':'class';
|
||||
if (isset($docs['deprecated'])) {
|
||||
echo " * *Warning: This $classType is deprecated, and should not longer be used.*\n";
|
||||
}
|
||||
if ($rClass->isInterface()) {
|
||||
echo " * This is an interface.\n";
|
||||
} elseif ($rClass->isAbstract()) {
|
||||
echo " * This is an abstract class.\n";
|
||||
}
|
||||
if (isset($docs['package'])) {
|
||||
$package = $docs['package'];
|
||||
if (isset($docs['subpackage'])) {
|
||||
$package.='_' . $docs['subpackage'];
|
||||
}
|
||||
if (!isset($packageList[$package])) {
|
||||
$packageList[$package] = array();
|
||||
}
|
||||
$packageList[$package][] = $rClass->getName();
|
||||
}
|
||||
|
||||
if ($extendedBy) {
|
||||
|
||||
echo "\n";
|
||||
if ($classType==='interface') {
|
||||
echo "This interface is extended by the following interfaces:\n";
|
||||
foreach($extendedBy as $className) {
|
||||
if (interface_exists($className)) {
|
||||
echo " * [" . $className . "]\n";
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
echo "This interface is implemented by the following classes:\n";
|
||||
} else {
|
||||
echo "This class is extended by the following classes:\n";
|
||||
}
|
||||
foreach($extendedBy as $className) {
|
||||
if (class_exists($className)) {
|
||||
echo " * [" . $className . "]\n";
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
echo "==Properties==\n";
|
||||
|
||||
echo "\n";
|
||||
|
||||
$properties = $rClass->getProperties(ReflectionProperty::IS_STATIC | ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED);
|
||||
|
||||
if (count($properties)>0) {
|
||||
foreach($properties as $rProperty) {
|
||||
|
||||
createPropertyDoc($rProperty);
|
||||
|
||||
}
|
||||
} else {
|
||||
echo "This $classType does not define any public or protected properties.\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
echo "==Methods==\n";
|
||||
|
||||
echo "\n";
|
||||
|
||||
$methods = $rClass->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED);
|
||||
|
||||
if (count($methods)>0) {
|
||||
foreach($methods as $rMethod) {
|
||||
|
||||
createMethodDoc($rMethod, $rClass);
|
||||
|
||||
}
|
||||
} else {
|
||||
echo "\nThis $classType does not define any public or protected methods.\n";
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
|
||||
}
|
||||
|
||||
function createMethodDoc($rMethod, $rClass) {
|
||||
|
||||
echo "===`" . $rMethod->getName() . "`===\n";
|
||||
echo "\n";
|
||||
|
||||
$docs = parseDocs($rMethod->getDocComment());
|
||||
|
||||
$return = isset($docs['return'])?$docs['return']:'void';
|
||||
|
||||
echo "{{{\n";
|
||||
echo $return . " " . $rMethod->class . "::" . $rMethod->getName() . "(";
|
||||
foreach($rMethod->getParameters() as $parameter) {
|
||||
if ($parameter->getPosition()>0) echo ", ";
|
||||
if ($class = $parameter->getClass()) {
|
||||
echo $class->name . " ";
|
||||
} elseif (isset($docs['param'][$parameter->name])) {
|
||||
echo $docs['param'][$parameter->name] . " ";
|
||||
}
|
||||
|
||||
echo '$' . $parameter->name;
|
||||
|
||||
if ($parameter->isOptional() && $parameter->isDefaultValueAvailable()) {
|
||||
$default = $parameter->getDefaultValue();
|
||||
$default = var_export($default,true);
|
||||
$default = str_replace("\n","",$default);
|
||||
echo " = " . $default;
|
||||
|
||||
}
|
||||
}
|
||||
echo ")\n";
|
||||
echo "}}}\n";
|
||||
echo "\n";
|
||||
|
||||
echo $docs['description'] . "\n";
|
||||
|
||||
echo "\n";
|
||||
|
||||
$hasProp = false;
|
||||
if (isset($docs['deprecated'])) {
|
||||
echo " * *Warning: This method is deprecated, and should not longer be used.*\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
if ($rMethod->isProtected()) {
|
||||
echo " * This method is protected.\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
if ($rMethod->isPrivate()) {
|
||||
echo " * This method is private.\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
if ($rMethod->isAbstract()) {
|
||||
echo " * This is an abstract method\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
|
||||
if ($rMethod->class != $rClass->name) {
|
||||
echo " * Defined in [" . $rMethod->class . "]\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
|
||||
if ($hasProp) echo "\n";
|
||||
|
||||
}
|
||||
|
||||
function createPropertyDoc($rProperty) {
|
||||
|
||||
echo "===`" . $rProperty->getName() . "`===\n";
|
||||
echo "\n";
|
||||
|
||||
$docs = parseDocs($rProperty->getDocComment());
|
||||
|
||||
$visibility = 'public';
|
||||
if ($rProperty->isProtected()) $visibility = 'protected';
|
||||
if ($rProperty->isPrivate()) $visibility = 'private';
|
||||
|
||||
echo "{{{\n";
|
||||
echo $visibility . " " . $rProperty->class . "::$" . $rProperty->getName();
|
||||
echo "\n}}}\n";
|
||||
echo "\n";
|
||||
|
||||
echo $docs['description'] . "\n";
|
||||
|
||||
echo "\n";
|
||||
|
||||
$hasProp = false;
|
||||
if (isset($docs['deprecated'])) {
|
||||
echo " * *Warning: This property is deprecated, and should not longer be used.*\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
if ($rProperty->isProtected()) {
|
||||
echo " * This property is protected.\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
if ($rProperty->isPrivate()) {
|
||||
echo " * This property is private.\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
if ($rProperty->isStatic()) {
|
||||
echo " * This property is static.\n";
|
||||
$hasProp = true;
|
||||
}
|
||||
|
||||
if ($hasProp) echo "\n";
|
||||
|
||||
}
|
||||
|
||||
function parseDocs($docString) {
|
||||
|
||||
$params = array();
|
||||
$description = array();
|
||||
|
||||
// Trimming all the comment characters
|
||||
$docString = trim($docString,"\n*/ ");
|
||||
$docString = explode("\n",$docString);
|
||||
|
||||
foreach($docString as $str) {
|
||||
|
||||
$str = ltrim($str,'* ');
|
||||
$str = trim($str);
|
||||
if ($str && $str[0]==='@') {
|
||||
$r = explode(' ',substr($str,1),2);
|
||||
$paramName = $r[0];
|
||||
$paramValue = (count($r)>1)?$r[1]:'';
|
||||
|
||||
// 'param' paramName is special. Confusing, I know.
|
||||
if ($paramName==='param') {
|
||||
if (!isset($params['param'])) $params['param'] = array();
|
||||
$paramValue = explode(' ', $paramValue,3);
|
||||
$params['param'][substr($paramValue[1],1)] = $paramValue[0];
|
||||
} else {
|
||||
$params[$paramName] = trim($paramValue);
|
||||
}
|
||||
} else {
|
||||
$description[]=$str;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$params['description'] = trim(implode("\n",$description),"\n ");
|
||||
|
||||
return $params;
|
||||
|
||||
}
|
||||
|
||||
function createSidebarIndex($packageList) {
|
||||
|
||||
ob_start();
|
||||
echo "#labels APIDocs\n";
|
||||
echo "#summary List of all classes, neatly organized\n";
|
||||
echo "=API Index=\n";
|
||||
|
||||
foreach($packageList as $package=>$classes) {
|
||||
|
||||
echo " * $package\n";
|
||||
sort($classes);
|
||||
foreach($classes as $class) {
|
||||
|
||||
echo " * [$class $class]\n";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
|
||||
}
|
232
dav/SabreDAV/bin/migrateto17.php
Normal file
232
dav/SabreDAV/bin/migrateto17.php
Normal file
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
|
||||
echo "SabreDAV migrate script for version 1.7\n";
|
||||
|
||||
if ($argc<2) {
|
||||
|
||||
echo <<<HELLO
|
||||
|
||||
This script help you migrate from a pre-1.7 database to 1.7 and later\n
|
||||
It is important to note, that this script only touches the 'calendarobjects'
|
||||
table.
|
||||
|
||||
If you do not have this table, or don't use the default PDO CalDAV backend
|
||||
it's pointless to run this script.
|
||||
|
||||
Keep in mind that some processing will be done on every single record of this
|
||||
table and in addition, ALTER TABLE commands will be executed.
|
||||
If you have a large calendarobjects table, this may mean that this process
|
||||
takes a while.
|
||||
|
||||
Usage:
|
||||
|
||||
{$argv[0]} [pdo-dsn] [username] [password]
|
||||
|
||||
For example:
|
||||
|
||||
{$argv[0]} mysql:host=localhost;dbname=sabredav root password
|
||||
{$argv[0]} sqlite:data/sabredav.db
|
||||
|
||||
HELLO;
|
||||
|
||||
exit();
|
||||
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . '/../lib/Sabre/VObject/includes.php')) {
|
||||
include __DIR__ . '/../lib/Sabre/VObject/includes.php';
|
||||
} else {
|
||||
// If, for some reason VObject was not found in the vicinity,
|
||||
// we'll try to grab it from the default path.
|
||||
require 'Sabre/VObject/includes.php';
|
||||
}
|
||||
|
||||
$dsn = $argv[1];
|
||||
$user = isset($argv[2])?$argv[2]:null;
|
||||
$pass = isset($argv[3])?$argv[3]:null;
|
||||
|
||||
echo "Connecting to database: " . $dsn . "\n";
|
||||
|
||||
$pdo = new PDO($dsn, $user, $pass);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
|
||||
echo "Validating existing table layout\n";
|
||||
|
||||
// The only cross-db way to do this, is to just fetch a single record.
|
||||
$row = $pdo->query("SELECT * FROM calendarobjects LIMIT 1")->fetch();
|
||||
|
||||
if (!$row) {
|
||||
echo "Error: This database did not have any records in the calendarobjects table, you should just recreate the table.\n";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
$requiredFields = array(
|
||||
'id',
|
||||
'calendardata',
|
||||
'uri',
|
||||
'calendarid',
|
||||
'lastmodified',
|
||||
);
|
||||
|
||||
foreach($requiredFields as $requiredField) {
|
||||
if (!array_key_exists($requiredField,$row)) {
|
||||
echo "Error: The current 'calendarobjects' table was missing a field we expected to exist.\n";
|
||||
echo "For safety reasons, this process is stopped.\n";
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
$fields17 = array(
|
||||
'etag',
|
||||
'size',
|
||||
'componenttype',
|
||||
'firstoccurence',
|
||||
'lastoccurence',
|
||||
);
|
||||
|
||||
$found = 0;
|
||||
foreach($fields17 as $field) {
|
||||
if (array_key_exists($field, $row)) {
|
||||
$found++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($found === 0) {
|
||||
echo "The database had the 1.6 schema. Table will now be altered.\n";
|
||||
echo "This may take some time for large tables\n";
|
||||
$pdo->exec(<<<SQL
|
||||
ALTER TABLE calendarobjects
|
||||
ADD etag VARCHAR(32),
|
||||
ADD size INT(11) UNSIGNED,
|
||||
ADD componenttype VARCHAR(8),
|
||||
ADD firstoccurence INT(11) UNSIGNED,
|
||||
ADD lastoccurence INT(11) UNSIGNED
|
||||
SQL
|
||||
);
|
||||
echo "Database schema upgraded.\n";
|
||||
|
||||
} elseif ($found === 5) {
|
||||
|
||||
echo "Database already had the 1.7 schema\n";
|
||||
|
||||
} else {
|
||||
|
||||
echo "The database had $found out of 5 from the changes for 1.7. This is scary and unusual, so we have to abort.\n";
|
||||
echo "You can manually try to upgrade the schema, and then run this script again.\n";
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
echo "Now, we need to parse every record and pull out some information.\n";
|
||||
|
||||
$result = $pdo->query('SELECT id, calendardata FROM calendarobjects');
|
||||
$stmt = $pdo->prepare('UPDATE calendarobjects SET etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE id = ?');
|
||||
|
||||
echo "Total records found: " . $result->rowCount() . "\n";
|
||||
$done = 0;
|
||||
$total = $result->rowCount();
|
||||
while($row = $result->fetch()) {
|
||||
|
||||
try {
|
||||
$newData = getDenormalizedData($row['calendardata']);
|
||||
} catch (Exception $e) {
|
||||
echo "===\nException caught will trying to parser calendarobject.\n";
|
||||
echo "Error message: " . $e->getMessage() . "\n";
|
||||
echo "Record id: " . $row['id'] . "\n";
|
||||
echo "This record is ignored, you should inspect it to see if there's anything wrong.\n===\n";
|
||||
continue;
|
||||
}
|
||||
$stmt->execute(array(
|
||||
$newData['etag'],
|
||||
$newData['size'],
|
||||
$newData['componentType'],
|
||||
$newData['firstOccurence'],
|
||||
$newData['lastOccurence'],
|
||||
$row['id'],
|
||||
));
|
||||
$done++;
|
||||
|
||||
if ($done % 500 === 0) {
|
||||
echo "Completed: $done / $total\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "Process completed!\n";
|
||||
|
||||
/**
|
||||
* Parses some information from calendar objects, used for optimized
|
||||
* calendar-queries.
|
||||
*
|
||||
* Blantently copied from Sabre_CalDAV_Backend_PDO
|
||||
*
|
||||
* Returns an array with the following keys:
|
||||
* * etag
|
||||
* * size
|
||||
* * componentType
|
||||
* * firstOccurence
|
||||
* * lastOccurence
|
||||
*
|
||||
* @param string $calendarData
|
||||
* @return array
|
||||
*/
|
||||
function getDenormalizedData($calendarData) {
|
||||
|
||||
$vObject = Sabre_VObject_Reader::read($calendarData);
|
||||
$componentType = null;
|
||||
$component = null;
|
||||
$firstOccurence = null;
|
||||
$lastOccurence = null;
|
||||
foreach($vObject->getComponents() as $component) {
|
||||
if ($component->name!=='VTIMEZONE') {
|
||||
$componentType = $component->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$componentType) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
|
||||
}
|
||||
if ($componentType === 'VEVENT') {
|
||||
$firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
|
||||
// Finding the last occurence is a bit harder
|
||||
if (!isset($component->RRULE)) {
|
||||
if (isset($component->DTEND)) {
|
||||
$lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
|
||||
} elseif (isset($component->DURATION)) {
|
||||
$endDate = clone $component->DTSTART->getDateTime();
|
||||
$endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value));
|
||||
$lastOccurence = $endDate->getTimeStamp();
|
||||
} elseif ($component->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) {
|
||||
$endDate = clone $component->DTSTART->getDateTime();
|
||||
$endDate->modify('+1 day');
|
||||
$lastOccurence = $endDate->getTimeStamp();
|
||||
} else {
|
||||
$lastOccurence = $firstOccurence;
|
||||
}
|
||||
} else {
|
||||
$it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID);
|
||||
$maxDate = new DateTime(self::MAX_DATE);
|
||||
if ($it->isInfinite()) {
|
||||
$lastOccurence = $maxDate->getTimeStamp();
|
||||
} else {
|
||||
$end = $it->getDtEnd();
|
||||
while($it->valid() && $end < $maxDate) {
|
||||
$end = $it->getDtEnd();
|
||||
$it->next();
|
||||
|
||||
}
|
||||
$lastOccurence = $end->getTimeStamp();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'etag' => md5($calendarData),
|
||||
'size' => strlen($calendarData),
|
||||
'componentType' => $componentType,
|
||||
'firstOccurence' => $firstOccurence,
|
||||
'lastOccurence' => $lastOccurence,
|
||||
);
|
||||
|
||||
}
|
140
dav/SabreDAV/bin/naturalselection.py
Executable file
140
dav/SabreDAV/bin/naturalselection.py
Executable file
|
@ -0,0 +1,140 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# Copyright (c) 2009-2010 Evert Pot
|
||||
# All rights reserved.
|
||||
# http://www.rooftopsolutions.nl/
|
||||
#
|
||||
# This utility is distributed along with SabreDAV
|
||||
# license: http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
|
||||
import os
|
||||
from optparse import OptionParser
|
||||
import time
|
||||
|
||||
def getfreespace(path):
|
||||
stat = os.statvfs(path)
|
||||
return stat.f_frsize * stat.f_bavail
|
||||
|
||||
def getbytesleft(path,treshold):
|
||||
return getfreespace(path)-treshold
|
||||
|
||||
def run(cacheDir, treshold, sleep=5, simulate=False, min_erase = 0):
|
||||
|
||||
bytes = getbytesleft(cacheDir,treshold)
|
||||
if (bytes>0):
|
||||
print "Bytes to go before we hit treshhold:", bytes
|
||||
else:
|
||||
print "Treshold exceeded with:", -bytes, "bytes"
|
||||
dir = os.listdir(cacheDir)
|
||||
dir2 = []
|
||||
for file in dir:
|
||||
path = cacheDir + '/' + file
|
||||
dir2.append({
|
||||
"path" : path,
|
||||
"atime": os.stat(path).st_atime,
|
||||
"size" : os.stat(path).st_size
|
||||
})
|
||||
|
||||
dir2.sort(lambda x,y: int(x["atime"]-y["atime"]))
|
||||
|
||||
filesunlinked = 0
|
||||
gainedspace = 0
|
||||
|
||||
# Left is the amount of bytes that need to be freed up
|
||||
# The default is the 'min_erase setting'
|
||||
left = min_erase
|
||||
|
||||
# If the min_erase setting is lower than the amount of bytes over
|
||||
# the treshold, we use that number instead.
|
||||
if left < -bytes :
|
||||
left = -bytes
|
||||
|
||||
print "Need to delete at least:", left;
|
||||
|
||||
for file in dir2:
|
||||
|
||||
# Only deleting files if we're not simulating
|
||||
if not simulate: os.unlink(file["path"])
|
||||
left = int(left - file["size"])
|
||||
gainedspace = gainedspace + file["size"]
|
||||
filesunlinked = filesunlinked + 1
|
||||
|
||||
if(left<0):
|
||||
break
|
||||
|
||||
print "%d files deleted (%d bytes)" % (filesunlinked, gainedspace)
|
||||
|
||||
|
||||
time.sleep(sleep)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = OptionParser(
|
||||
version="naturalselecton v0.3",
|
||||
description="Cache directory manager. Deletes cache entries based on accesstime and free space tresholds.\n" +
|
||||
"This utility is distributed alongside SabreDAV.",
|
||||
usage="usage: %prog [options] cacheDirectory",
|
||||
)
|
||||
parser.add_option(
|
||||
'-s',
|
||||
dest="simulate",
|
||||
action="store_true",
|
||||
help="Don't actually make changes, but just simulate the behaviour",
|
||||
)
|
||||
parser.add_option(
|
||||
'-r','--runs',
|
||||
help="How many times to check before exiting. -1 is infinite, which is the default",
|
||||
type="int",
|
||||
dest="runs",
|
||||
default=-1
|
||||
)
|
||||
parser.add_option(
|
||||
'-n','--interval',
|
||||
help="Sleep time in seconds (default = 5)",
|
||||
type="int",
|
||||
dest="sleep",
|
||||
default=5
|
||||
)
|
||||
parser.add_option(
|
||||
'-l','--treshold',
|
||||
help="Treshhold in bytes (default = 10737418240, which is 10GB)",
|
||||
type="int",
|
||||
dest="treshold",
|
||||
default=10737418240
|
||||
)
|
||||
parser.add_option(
|
||||
'-m', '--min-erase',
|
||||
help="Minimum number of bytes to erase when the treshold is reached. " +
|
||||
"Setting this option higher will reduce the amount of times the cache directory will need to be scanned. " +
|
||||
"(the default is 1073741824, which is 1GB.)",
|
||||
type="int",
|
||||
dest="min_erase",
|
||||
default=1073741824
|
||||
)
|
||||
|
||||
options,args = parser.parse_args()
|
||||
if len(args)<1:
|
||||
parser.error("This utility requires at least 1 argument")
|
||||
cacheDir = args[0]
|
||||
|
||||
print "Natural Selection"
|
||||
print "Cache directory:", cacheDir
|
||||
free = getfreespace(cacheDir);
|
||||
print "Current free disk space:", free
|
||||
|
||||
runs = options.runs;
|
||||
while runs!=0 :
|
||||
run(
|
||||
cacheDir,
|
||||
sleep=options.sleep,
|
||||
simulate=options.simulate,
|
||||
treshold=options.treshold,
|
||||
min_erase=options.min_erase
|
||||
)
|
||||
if runs>0:
|
||||
runs = runs - 1
|
||||
|
||||
if __name__ == '__main__' :
|
||||
main()
|
321
dav/SabreDAV/bin/pearpackage3.php
Executable file
321
dav/SabreDAV/bin/pearpackage3.php
Executable file
|
@ -0,0 +1,321 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
$make = false;
|
||||
$packageName = null;
|
||||
|
||||
foreach($argv as $index=>$arg) {
|
||||
if ($index==0) continue;
|
||||
if ($arg=='make') {
|
||||
$make = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$packageName = $arg;
|
||||
}
|
||||
|
||||
if (is_null($packageName)) {
|
||||
echo "A packagename is required\n";
|
||||
die(1);
|
||||
}
|
||||
|
||||
if (!is_dir('build/' . $packageName)) {
|
||||
echo "Could not find package directory: build/$packageName\n";
|
||||
die(2);
|
||||
}
|
||||
|
||||
// We'll figure out something better for this one day
|
||||
|
||||
$dependencies = array(
|
||||
array(
|
||||
'type' => 'php',
|
||||
'min' => '5.3.1',
|
||||
),
|
||||
array(
|
||||
'type' => 'pearinstaller',
|
||||
'min' => '1.9',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
switch($packageName) {
|
||||
|
||||
case 'Sabre' :
|
||||
$summary = 'Sabretooth base package.';
|
||||
$description = <<<TEXT
|
||||
The base package provides some functionality used by all packages.
|
||||
|
||||
Currently this is only an autoloader
|
||||
TEXT;
|
||||
$version = '1.0.0';
|
||||
$stability = 'stable';
|
||||
break;
|
||||
|
||||
case 'Sabre_DAV' :
|
||||
$summary = 'Sabre_DAV is a WebDAV framework for PHP.';
|
||||
$description = <<<TEXT
|
||||
SabreDAV allows you to easily integrate WebDAV access into your existing PHP application.
|
||||
|
||||
Feature List:
|
||||
* Fully WebDAV (class 1, 2, 3) compliant
|
||||
* Supports Windows clients, OS/X, DavFS, Cadaver, and pretty much everything we've come accross
|
||||
* Custom property support
|
||||
* RFC4918-compliant
|
||||
* Authentication support
|
||||
* Plugin system
|
||||
TEXT;
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.0.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_HTTP',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.6.0',
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case 'Sabre_HTTP' :
|
||||
$summary = 'Sabre_HTTP provides various HTTP helpers, for input and output and authentication';
|
||||
$description = <<<TEXT
|
||||
Sabre_HTTP effectively wraps around \$_SERVER, php://input, php://output and the headers method,
|
||||
allowing for a central interface to deal with this as well as easier unittesting.
|
||||
|
||||
In addition Sabre_HTTP provides classes for Basic, Digest and Amazon AWS authentication.
|
||||
TEXT;
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.0.0',
|
||||
);
|
||||
break;
|
||||
|
||||
case 'Sabre_DAVACL' :
|
||||
$summary = 'Sabre_DAVACL provides rfc3744 support.';
|
||||
$description = <<<TEXT
|
||||
Sabre_DAVACL is the RFC3744 implementation for SabreDAV. It provides principals
|
||||
(users and groups) and access control.
|
||||
TEXT;
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.0.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_DAV',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.6.0',
|
||||
);
|
||||
break;
|
||||
|
||||
case 'Sabre_CalDAV' :
|
||||
$summary = 'Sabre_CalDAV provides CalDAV extensions to SabreDAV';
|
||||
$description = <<<TEXT
|
||||
Sabre_CalDAV provides RFC4791 (CalDAV) support to Sabre_DAV.
|
||||
|
||||
Feature list:
|
||||
* Multi-user Calendar Server
|
||||
* Support for Apple iCal, Evolution, Sunbird, Lightning
|
||||
TEXT;
|
||||
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.0.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_HTTP',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.6.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_DAV',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.6.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_DAVACL',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.6.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_VObject',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.3.0',
|
||||
);
|
||||
break;
|
||||
|
||||
case 'Sabre_CardDAV' :
|
||||
$summary = 'Sabre_CardDAV provides CardDAV extensions to SabreDAV';
|
||||
$description = <<<TEXT
|
||||
Sabre_CardDAV provides CardDAV support to Sabre_DAV.
|
||||
|
||||
Feature list:
|
||||
* Multi-user addressbook server
|
||||
* ACL support
|
||||
* Support for OS/X, iOS, Evolution and probably more
|
||||
* Hook-ins for creating a global \'directory\'.
|
||||
TEXT;
|
||||
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.0.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_HTTP',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.6.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_DAV',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.6.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_DAVACL',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.6.0',
|
||||
);
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre_VObject',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.3.0',
|
||||
);
|
||||
break;
|
||||
|
||||
case 'Sabre_VObject' :
|
||||
$summary = 'Sabre_VObject is a natural-interface iCalendar and vCard reader';
|
||||
$description = <<<TEXT
|
||||
Sabre_VObject is an intuitive reader for iCalendar and vCard objects.
|
||||
|
||||
It provides a natural array/object accessor interface to the parsed tree, much like
|
||||
simplexml for XML files.
|
||||
TEXT;
|
||||
$dependencies[] = array(
|
||||
'type' => 'package',
|
||||
'name' => 'Sabre',
|
||||
'channel' => 'pear.sabredav.org',
|
||||
'min' => '1.0.0',
|
||||
);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (!isset($version)) {
|
||||
include 'lib/' . str_replace('_','/',$packageName) . '/Version.php';
|
||||
$versionClassName = $packageName . '_Version';
|
||||
$version = $versionClassName::VERSION;
|
||||
$stability = $versionClassName::STABILITY;
|
||||
}
|
||||
|
||||
$lead = 'Evert Pot';
|
||||
$lead_email = 'evert@rooftopsolutions.nl';
|
||||
$date = date('Y-m-d');
|
||||
|
||||
$license = 'Modified BSD';
|
||||
$licenseuri = 'http://code.google.com/p/sabredav/wiki/License';
|
||||
$notes = 'New release. Read the ChangeLog and announcement for more details';
|
||||
$channel = 'pear.sabredav.org';
|
||||
|
||||
/* This function is intended to generate the full file list */
|
||||
function parsePath($fullPath, $role, $padding = 4) {
|
||||
|
||||
$fileList = '';
|
||||
$file = basename($fullPath);
|
||||
if (is_dir($fullPath)) {
|
||||
$fileList .= str_repeat(' ', $padding) . "<dir name=\"{$file}\">\n";
|
||||
foreach(scandir($fullPath) as $subPath) {;
|
||||
if ($subPath==='.' || $subPath==='..') continue;
|
||||
$fileList .= parsePath($fullPath. '/' . $subPath,$role, $padding+2);
|
||||
}
|
||||
$fileList .= str_repeat(' ', $padding) . "</dir><!-- {$file} -->\n";
|
||||
} elseif (is_file($fullPath)) {
|
||||
$fileList .= str_repeat(' ', $padding) . "<file name=\"{$file}\" role=\"{$role}\" />\n";
|
||||
}
|
||||
|
||||
return $fileList;
|
||||
|
||||
}
|
||||
|
||||
$rootDir = realpath('build/' . $packageName);
|
||||
|
||||
$fileList = parsePath($rootDir . '/Sabre', 'php');
|
||||
$fileList .= parsePath($rootDir . '/examples', 'doc');
|
||||
$fileList .= parsePath($rootDir . '/ChangeLog', 'doc');
|
||||
$fileList .= parsePath($rootDir . '/LICENSE', 'doc');
|
||||
|
||||
$dependenciesXML = "\n";
|
||||
foreach($dependencies as $dep) {
|
||||
$pad = 8;
|
||||
$dependenciesXML.=str_repeat(' ',$pad) . '<' . $dep['type'] . ">\n";
|
||||
foreach($dep as $key=>$value) {
|
||||
if ($key=='type') continue;
|
||||
$dependenciesXML.=str_repeat(' ',$pad+2) . "<$key>$value</$key>\n";
|
||||
}
|
||||
$dependenciesXML.=str_repeat(' ',$pad) . '</' . $dep['type'] . ">\n";
|
||||
}
|
||||
|
||||
$package = <<<XML
|
||||
<?xml version="1.0"?>
|
||||
<package version="2.0"
|
||||
xmlns="http://pear.php.net/dtd/package-2.0">
|
||||
|
||||
<name>{$packageName}</name>
|
||||
<channel>{$channel}</channel>
|
||||
<summary>{$summary}</summary>
|
||||
<description>{$description}</description>
|
||||
<lead>
|
||||
<name>{$lead}</name>
|
||||
<user>{$lead}</user>
|
||||
<email>{$lead_email}</email>
|
||||
<active>true</active>
|
||||
</lead>
|
||||
<date>{$date}</date>
|
||||
<version>
|
||||
<release>{$version}</release>
|
||||
<api>{$version}</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>{$stability}</release>
|
||||
<api>{$stability}</api>
|
||||
</stability>
|
||||
<license uri="{$licenseuri}">{$license}</license>
|
||||
<notes>{$notes}</notes>
|
||||
<contents>
|
||||
<dir name="/">{$fileList}
|
||||
</dir>
|
||||
</contents>
|
||||
<dependencies>
|
||||
<required>{$dependenciesXML}
|
||||
</required>
|
||||
</dependencies>
|
||||
<phprelease />
|
||||
</package>
|
||||
XML;
|
||||
|
||||
if (isset($argv) && in_array('make',$argv)) {
|
||||
file_put_contents($rootDir . '/package.xml',$package);
|
||||
} else {
|
||||
echo $package;
|
||||
}
|
268
dav/SabreDAV/build.xml
Normal file
268
dav/SabreDAV/build.xml
Normal file
|
@ -0,0 +1,268 @@
|
|||
<?xml version="1.0"?>
|
||||
<project name="SabreDAV" default="build" basedir=".">
|
||||
|
||||
<!-- Any default properties -->
|
||||
<property file="build.properties" />
|
||||
|
||||
<!-- Where to write api documentation -->
|
||||
<property name="sabredav.apidocspath" value="docs/api" />
|
||||
|
||||
<target name="build" depends="init, test, clean">
|
||||
<mkdir dir="build" />
|
||||
|
||||
<echo msg="Building Sabre pear package" />
|
||||
<mkdir dir="build/Sabre" />
|
||||
<copy todir="build/Sabre">
|
||||
<fileset dir="lib">
|
||||
<include name="Sabre/autoload.php" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy todir="build/Sabre">
|
||||
<fileset dir=".">
|
||||
<include name="LICENSE" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<exec command="bin/pearpackage3.php make Sabre" checkreturn="true" />
|
||||
<exec command="pear package" dir="build/Sabre" checkreturn="true" />
|
||||
|
||||
<echo msg="Building Sabre_HTTP pear package" />
|
||||
<mkdir dir="build/Sabre_HTTP" />
|
||||
<mkdir dir="build/Sabre_HTTP/Sabre" />
|
||||
<copy todir="build/Sabre_HTTP" includeemptydirs="true" >
|
||||
<fileset dir="lib">
|
||||
<include name="Sabre/HTTP/**" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy todir="build/Sabre_HTTP">
|
||||
<fileset dir=".">
|
||||
<include name="LICENSE" />
|
||||
<include name="ChangeLog" />
|
||||
<include name="examples/basicauth.php" />
|
||||
<include name="examples/digestauth.php" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<exec command="bin/pearpackage3.php make Sabre_HTTP" checkreturn="true" />
|
||||
<exec command="pear package" dir="build/Sabre_HTTP" checkreturn="true" />
|
||||
|
||||
<echo msg="Building Sabre_DAV pear package" />
|
||||
<mkdir dir="build/Sabre_DAV" />
|
||||
<mkdir dir="build/Sabre_DAV/Sabre" />
|
||||
<copy todir="build/Sabre_DAV" includeemptydirs="true" >
|
||||
<fileset dir="lib">
|
||||
<include name="Sabre/DAV/**" />
|
||||
<exclude name="Sabre/DAVACL/**" />
|
||||
<include name="Sabre.includes.php" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy todir="build/Sabre_DAV" includeemptydirs="true">
|
||||
<fileset dir="." >
|
||||
<include name="LICENSE" />
|
||||
<include name="ChangeLog" />
|
||||
<include name="examples/fileserver.php" />
|
||||
<include name="examples/simplefsserver.php" />
|
||||
<include name="examples/sql/*.locks.sql" />
|
||||
<include name="examples/sql/*.users.sql" />
|
||||
<include name="examples/webserver/*.conf" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<exec command="bin/pearpackage3.php make Sabre_DAV" checkreturn="true" />
|
||||
<exec command="pear package" dir="build/Sabre_DAV" checkreturn="true" />
|
||||
|
||||
<!-- DAVACL -->
|
||||
<echo msg="Building Sabre_DAVACL pear package" />
|
||||
<mkdir dir="build/Sabre_DAVACL" />
|
||||
<mkdir dir="build/Sabre_DAVACL/Sabre" />
|
||||
<copy todir="build/Sabre_DAVACL" includeemptydirs="true" >
|
||||
<fileset dir="lib">
|
||||
<include name="Sabre/DAVACL/**" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<mkdir dir="build/Sabre_DAVACL" />
|
||||
<copy todir="build/Sabre_DAVACL">
|
||||
<fileset dir=".">
|
||||
<include name="LICENSE" />
|
||||
<include name="ChangeLog" />
|
||||
<include name="examples/sql/*.principals.sql" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<exec command="bin/pearpackage3.php make Sabre_DAVACL" checkreturn="true" />
|
||||
<exec command="pear package" dir="build/Sabre_DAVACL" checkreturn="true" />
|
||||
|
||||
<!-- CalDAV -->
|
||||
<echo msg="Building Sabre_CalDAV pear package" />
|
||||
<mkdir dir="build/Sabre_CalDAV" />
|
||||
<mkdir dir="build/Sabre_CalDAV/Sabre" />
|
||||
<copy todir="build/Sabre_CalDAV" includeemptydirs="true" >
|
||||
<fileset dir="lib">
|
||||
<include name="Sabre/CalDAV/**" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<mkdir dir="build/Sabre_CalDAV" />
|
||||
<copy todir="build/Sabre_CalDAV">
|
||||
<fileset dir=".">
|
||||
<include name="LICENSE" />
|
||||
<include name="ChangeLog" />
|
||||
<include name="examples/calendarserver.php" />
|
||||
<include name="examples/sql/*.calendars.sql" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<exec command="bin/pearpackage3.php make Sabre_CalDAV" checkreturn="true" />
|
||||
<exec command="pear package" dir="build/Sabre_CalDAV" checkreturn="true" />
|
||||
|
||||
<!-- CardDAV -->
|
||||
<echo msg="Building Sabre_CardDAV pear package" />
|
||||
<mkdir dir="build/Sabre_CardDAV" />
|
||||
<mkdir dir="build/Sabre_CardDAV/Sabre" />
|
||||
<copy todir="build/Sabre_CardDAV" includeemptydirs="true" >
|
||||
<fileset dir="lib">
|
||||
<include name="Sabre/CardDAV/**" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<mkdir dir="build/Sabre_CardDAV" />
|
||||
<copy todir="build/Sabre_CardDAV">
|
||||
<fileset dir=".">
|
||||
<include name="LICENSE" />
|
||||
<include name="ChangeLog" />
|
||||
<include name="examples/addressbookserver.php" />
|
||||
<include name="examples/sql/*.addressbooks.sql" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<exec command="bin/pearpackage3.php make Sabre_CardDAV" checkreturn="true" />
|
||||
<exec command="pear package" dir="build/Sabre_CardDAV" checkreturn="true" />
|
||||
|
||||
<!-- VObject -->
|
||||
<echo msg="Building Sabre_VObject pear package" />
|
||||
<mkdir dir="build/Sabre_VObject" />
|
||||
<mkdir dir="build/Sabre_VObject/Sabre" />
|
||||
<copy todir="build/Sabre_VObject" includeemptydirs="true" >
|
||||
<fileset dir="lib">
|
||||
<include name="Sabre/VObject/**" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<mkdir dir="build/Sabre_VObject" />
|
||||
<copy todir="build/Sabre_VObject">
|
||||
<fileset dir=".">
|
||||
<include name="LICENSE" />
|
||||
<include name="ChangeLog" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<exec command="bin/pearpackage3.php make Sabre_VObject" checkreturn="true" />
|
||||
<exec command="pear package" dir="build/Sabre_VObject" checkreturn="true" />
|
||||
|
||||
<!-- moving tgz files -->
|
||||
<move todir="build">
|
||||
<mapper type="flatten" />
|
||||
<fileset dir="build/">
|
||||
<include name="**/*.tgz" />
|
||||
</fileset>
|
||||
</move>
|
||||
|
||||
|
||||
<echo>Creating combined SabreDAV build</echo>
|
||||
<mkdir dir="build/SabreDAV" />
|
||||
<mkdir dir="build/SabreDAV/lib" />
|
||||
<mkdir dir="build/SabreDAV/lib/Sabre" />
|
||||
<mkdir dir="build/SabreDAV/lib/Sabre/CalDAV" />
|
||||
<mkdir dir="build/SabreDAV/lib/Sabre/DAV" />
|
||||
<mkdir dir="build/SabreDAV/lib/Sabre/DAV/Auth" />
|
||||
<mkdir dir="build/SabreDAV/lib/Sabre/DAV/Locks" />
|
||||
<mkdir dir="build/SabreDAV/lib/Sabre/HTTP" />
|
||||
<mkdir dir="build/SabreDAV/lib/Sabre/VObject" />
|
||||
<mkdir dir="build/SabreDAV/tests" />
|
||||
<mkdir dir="build/SabreDAV/tests/Sabre" />
|
||||
<mkdir dir="build/SabreDAV/tests/Sabre/CalDAV" />
|
||||
<mkdir dir="build/SabreDAV/tests/Sabre/DAV" />
|
||||
<mkdir dir="build/SabreDAV/tests/Sabre/HTTP" />
|
||||
<mkdir dir="build/SabreDAV/tests/Sabre/DAV/Auth" />
|
||||
<mkdir dir="build/SabreDAV/tests/Sabre/DAV/Locks" />
|
||||
<mkdir dir="build/SabreDAV/tests/Sabre/VObject" />
|
||||
<copy todir="build/SabreDAV" includeemptydirs="true">
|
||||
<fileset dir=".">
|
||||
<include name="lib/**/*.php" />
|
||||
<include name="lib/Sabre/DAV/Browser/assets/**" />
|
||||
<include name="ChangeLog" />
|
||||
<include name="LICENSE" />
|
||||
<include name="examples/**.php" />
|
||||
<include name="examples/**/*.sql" />
|
||||
<include name="bin/naturalselection.py" />
|
||||
<include name="bin/migrateto17.php" />
|
||||
<include name="tests/**/*.xml" />
|
||||
<include name="tests/**/*.php" />
|
||||
</fileset>
|
||||
</copy>
|
||||
<mkdir dir="build/SabreDAV/tests/temp" />
|
||||
<zip destfile="build/SabreDAV-${sabredav.version}.zip" basedir="build/SabreDAV" prefix="SabreDAV/" />
|
||||
|
||||
</target>
|
||||
|
||||
<target name="clean" depends="init">
|
||||
<echo msg="Removing build files (cleaning up distribution)" />
|
||||
<delete dir="docs/api" />
|
||||
<delete dir="build" />
|
||||
</target>
|
||||
|
||||
<target name="release" depends="init,clean,test,build">
|
||||
<echo>Creating Git release tag</echo>
|
||||
<exec command="git tag ${sabredav.version}" checkreturn="false" passthru="1" />
|
||||
<echo>Uploading to Google Code</echo>
|
||||
<propertyprompt propertyName="googlecode.username" promptText="Enter your googlecode username" useExistingValue="true" />
|
||||
<propertyprompt propertyName="googlecode.password" promptText="Enter your googlecode password" useExistingValue="true" />
|
||||
<exec command="bin/googlecode_upload.py -s 'SabreDAV ${sabredav.version}' -p sabredav --labels=${sabredav.ucstability} -u '${googlecode.username}' -w '${googlecode.password}' build/SabreDAV-${sabredav.version}.zip" checkreturn="true" />
|
||||
</target>
|
||||
|
||||
<target name="test">
|
||||
<phpunit haltonfailure="1" haltonerror="1" bootstrap="tests/bootstrap.php" haltonskipped="1" printsummary="1">
|
||||
<batchtest>
|
||||
<fileset dir="tests">
|
||||
<include name="**/*.php"/>
|
||||
</fileset>
|
||||
</batchtest>
|
||||
</phpunit>
|
||||
</target>
|
||||
|
||||
<target name="apidocs" depends="init">
|
||||
|
||||
<echo>Creating api documentation using PHP documentor</echo>
|
||||
<echo>Writing to ${sabredav.apidocspath}</echo>
|
||||
<phpdoc title="SabreDAV API documentation"
|
||||
destdir="${sabredav.apidocspath}"
|
||||
sourcecode="false"
|
||||
output="HTML:frames:phphtmllib">
|
||||
|
||||
<fileset dir="./lib">
|
||||
<include name="**/*.php" />
|
||||
</fileset>
|
||||
<projdocfileset dir=".">
|
||||
<include name="ChangeLog" />
|
||||
<include name="LICENSE" />
|
||||
</projdocfileset>
|
||||
|
||||
</phpdoc>
|
||||
|
||||
</target>
|
||||
|
||||
<target name="init">
|
||||
|
||||
<!-- This sets SabreDAV version information -->
|
||||
<adhoc-task name="sabredav-version"><![CDATA[
|
||||
|
||||
class SabreDAV_VersionTask extends Task {
|
||||
|
||||
public function main() {
|
||||
|
||||
include_once 'lib/Sabre/DAV/Version.php';
|
||||
$this->getProject()->setNewProperty('sabredav.version',Sabre_DAV_Version::VERSION);
|
||||
$this->getProject()->setNewProperty('sabredav.stability',Sabre_DAV_Version::STABILITY);
|
||||
$this->getProject()->setNewProperty('sabredav.ucstability',ucwords(Sabre_DAV_Version::STABILITY));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
]]></adhoc-task>
|
||||
<sabredav-version />
|
||||
<echo>SabreDAV version ${sabredav.version}</echo>
|
||||
|
||||
</target>
|
||||
|
||||
</project>
|
21
dav/SabreDAV/composer.json
Normal file
21
dav/SabreDAV/composer.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "evert/sabredav",
|
||||
"type": "library",
|
||||
"description": "WebDAV Framework for PHP",
|
||||
"keywords": ["Framework", "WebDAV", "CalDAV", "CardDAV", "iCalendar"],
|
||||
"homepage": "http://code.google.com/p/sabredav/",
|
||||
"license": "New BSD License",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Evert Pot",
|
||||
"email": "evert@rooftopsolutions.nl",
|
||||
"homepage" : "http://www.rooftopsolutions.nl/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Sabre": "lib/" }
|
||||
}
|
||||
}
|
336
dav/SabreDAV/docs/caldav-ctag.txt
Normal file
336
dav/SabreDAV/docs/caldav-ctag.txt
Normal file
|
@ -0,0 +1,336 @@
|
|||
|
||||
|
||||
|
||||
Calendar Server Extension C. Daboo
|
||||
Apple
|
||||
May 3, 2007
|
||||
|
||||
|
||||
Calendar Collection Entity Tag (CTag) in CalDAV
|
||||
caldav-ctag-02
|
||||
|
||||
Abstract
|
||||
|
||||
This specification defines an extension to CalDAV that provides a
|
||||
fast way for a client to determine whether the contents of a calendar
|
||||
collection may have changed.
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
|
||||
2. Conventions Used in This Document . . . . . . . . . . . . . . . 2
|
||||
3. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3.1. Server . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3.2. Client . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
4. New features in CalDAV . . . . . . . . . . . . . . . . . . . . 3
|
||||
4.1. getctag WebDAV Property . . . . . . . . . . . . . . . . . . 4
|
||||
5. Security Considerations . . . . . . . . . . . . . . . . . . . . 4
|
||||
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
7. Normative References . . . . . . . . . . . . . . . . . . . . . 5
|
||||
Appendix A. Acknowledgments . . . . . . . . . . . . . . . . . . . 5
|
||||
Appendix B. Change History . . . . . . . . . . . . . . . . . . . . 5
|
||||
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 6
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 1]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
In CalDAV [RFC4791] calendar data is stored in calendar collection
|
||||
resources. Clients need to "poll" calendar collections in order to
|
||||
find out what has changed since the last time they examined it.
|
||||
Currently that involves having to do a PROPFIND Depth:1 HTTP request,
|
||||
or a CALDAV:calendar-query REPORT request. When a calendar
|
||||
collection contains a large number of calendar resources those
|
||||
operations become expensive on the server.
|
||||
|
||||
Calendar users often configure their clients to poll at short time
|
||||
intervals. So polling traffic to the server will be high, even
|
||||
though the frequency at which changes actually occur to a calendar is
|
||||
typically low.
|
||||
|
||||
To improve on performance, this specification defines a new "calendar
|
||||
collection entity tag" (CTag) WebDAV property that is defined on
|
||||
calendar collections. When the calendar collection changes, the CTag
|
||||
value changes. Thus a client can cache the CTag at some point in
|
||||
time, then poll the collection only (i.e. PROPFIND Depth:0 HTTP
|
||||
requests) and determine if a change has happened based on the
|
||||
returned CTag value. If there is a change, it can then fall back to
|
||||
doing the full (Depth:1) poll of the collection to actually determine
|
||||
which resources in the collection changed.
|
||||
|
||||
This extension also defines CTag's on CalDAV scheduling
|
||||
[I-D.desruisseaux-caldav-sched] Inbox and Outbox collections.
|
||||
|
||||
|
||||
2. Conventions Used in This Document
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
When XML element types in the namespaces "DAV:" and
|
||||
"urn:ietf:params:xml:ns:caldav" are referenced in this document
|
||||
outside of the context of an XML fragment, the string "DAV:" and
|
||||
"CALDAV:" will be prefixed to the element type names respectively.
|
||||
|
||||
The namespace "http://calendarserver.org/ns/" is used for XML
|
||||
elements defined in this specification. When XML element types in
|
||||
this namespace are referenced in this document outside of the context
|
||||
of an XML fragment, the string "CS:" will be prefixed to the element
|
||||
type names respectively.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 2]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
3. Overview
|
||||
|
||||
3.1. Server
|
||||
|
||||
For each calendar or scheduling Inbox or Outbox collection on the
|
||||
server, a new CS:getctag WebDAV property is present.
|
||||
|
||||
The property value is an "opaque" token whose value is guaranteed to
|
||||
be unique over the lifetime of any calendar or scheduling Inbox or
|
||||
Outbox collection at a specific URI.
|
||||
|
||||
Whenever a calendar resource is added to, modified or deleted from
|
||||
the calendar collection, the value of the CS:getctag property MUST
|
||||
change. Typically this change will occur when the DAV:getetag
|
||||
property on a child resource changes due to some protocol action. It
|
||||
could be the result of a change to the body or properties of the
|
||||
resource.
|
||||
|
||||
3.2. Client
|
||||
|
||||
The client starts off with an empty string as the initial value for
|
||||
the cached CTag of a calendar or scheduling Inbox or Outbox
|
||||
collection that it intends to synchronize with.
|
||||
|
||||
When polling a calendar or scheduling Inbox or Outbox collection, the
|
||||
client issues a PROPFIND Depth:0 HTTP request, asking for the CS:
|
||||
getctag property to be returned.
|
||||
|
||||
If the returned value of CS:getctag property matches the one
|
||||
currently cached for the calendar or scheduling Inbox or Outbox
|
||||
collection, then the collection contents have not changed and no
|
||||
further action is required until the next poll.
|
||||
|
||||
If the returned value of CS:getctag property does not match the one
|
||||
found previously, then the contents of the calendar or scheduling
|
||||
Inbox or Outbox collection have changed. At that point the client
|
||||
should re-issue the PROPFIND Depth:1 request to get the collection
|
||||
changes in detail and the CS:getctag property value corresponding to
|
||||
the new state. The new CSgetctag property value should replace the
|
||||
one currently cached for that calendar or scheduling Inbox or Outbox
|
||||
collection.
|
||||
|
||||
|
||||
4. New features in CalDAV
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 3]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
4.1. getctag WebDAV Property
|
||||
|
||||
Name: getctag
|
||||
|
||||
Namespace: http://calendarserver.org/ns/
|
||||
|
||||
Purpose: Specifies a "synchronization" token used to indicate when
|
||||
the contents of a calendar or scheduling Inbox or Outbox
|
||||
collection have changed.
|
||||
|
||||
Conformance: This property MUST be defined on a calendar or
|
||||
scheduling Inbox or Outbox collection resource. It MUST be
|
||||
protected and SHOULD be returned by a PROPFIND DAV:allprop request
|
||||
(as defined in Section 12.14.1 of [RFC2518]).
|
||||
|
||||
Description: The CS:getctag property allows clients to quickly
|
||||
determine if the contents of a calendar or scheduling Inbox or
|
||||
Outbox collection have changed since the last time a
|
||||
"synchronization" operation was done. The CS:getctag property
|
||||
value MUST change each time the contents of the calendar or
|
||||
scheduling Inbox or Outbox collection change, and each change MUST
|
||||
result in a value that is different from any other used with that
|
||||
collection URI.
|
||||
|
||||
Definition:
|
||||
|
||||
<!ELEMENT getctag #PCDATA>
|
||||
|
||||
Example:
|
||||
|
||||
<T:getctag xmlns:T="http://calendarserver.org/ns/"
|
||||
>ABCD-GUID-IN-THIS-COLLECTION-20070228T122324010340</T:getctag>
|
||||
|
||||
|
||||
5. Security Considerations
|
||||
|
||||
The CS:getctag property value changes whenever any resource in the
|
||||
collection or scheduling Inbox or Outbox changes. Thus a change to a
|
||||
resource that a user does not have read access to will result in a
|
||||
change in the CTag and the user will know that a change occurred.
|
||||
However, that user will not able to get additional details about
|
||||
exactly what changed as WebDAV ACLs [RFC3744] will prevent that. So
|
||||
this does expose the fact that there are potentially "hidden"
|
||||
resources in a calendar collection, but it does not expose any
|
||||
details about them.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 4]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
6. IANA Considerations
|
||||
|
||||
This document does not require any actions on the part of IANA.
|
||||
|
||||
|
||||
7. Normative References
|
||||
|
||||
[I-D.desruisseaux-caldav-sched]
|
||||
Desruisseaux, B., "Scheduling Extensions to CalDAV",
|
||||
draft-desruisseaux-caldav-sched-03 (work in progress),
|
||||
January 2007.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, S., and D.
|
||||
Jensen, "HTTP Extensions for Distributed Authoring --
|
||||
WEBDAV", RFC 2518, February 1999.
|
||||
|
||||
[RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web
|
||||
Distributed Authoring and Versioning (WebDAV) Access
|
||||
Control Protocol", RFC 3744, May 2004.
|
||||
|
||||
[RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault,
|
||||
"Calendaring Extensions to WebDAV (CalDAV)", RFC 4791,
|
||||
March 2007.
|
||||
|
||||
|
||||
Appendix A. Acknowledgments
|
||||
|
||||
This specification is the result of discussions between the Apple
|
||||
calendar server and client teams.
|
||||
|
||||
|
||||
Appendix B. Change History
|
||||
|
||||
Changes from -01:
|
||||
|
||||
1. Updated to RFC4791 reference.
|
||||
|
||||
2. Added text indicating that ctag applies to schedule Inbox and
|
||||
Outbox as well.
|
||||
|
||||
Changes from -00:
|
||||
|
||||
1. Relaxed requirement so that any type of change to a child
|
||||
resource can trigger a CTag change (similar behavior to ETag).
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 5]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
Author's Address
|
||||
|
||||
Cyrus Daboo
|
||||
Apple Inc.
|
||||
1 Infinite Loop
|
||||
Cupertino, CA 95014
|
||||
USA
|
||||
|
||||
Email: cyrus@daboo.name
|
||||
URI: http://www.apple.com/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 6]
|
||||
|
560
dav/SabreDAV/docs/caldav-proxy.txt
Normal file
560
dav/SabreDAV/docs/caldav-proxy.txt
Normal file
|
@ -0,0 +1,560 @@
|
|||
|
||||
|
||||
|
||||
Calendar Server Extension C. Daboo
|
||||
Apple Computer
|
||||
May 3, 2007
|
||||
|
||||
|
||||
Calendar User Proxy Functionality in CalDAV
|
||||
caldav-cu-proxy-02
|
||||
|
||||
Abstract
|
||||
|
||||
This specification defines an extension to CalDAV that makes it easy
|
||||
for clients to setup and manage calendar user proxies, using the
|
||||
WebDAV Access Control List extension as a basis.
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
|
||||
2. Conventions Used in This Document . . . . . . . . . . . . . . 2
|
||||
3. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3.1. Server . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3.2. Client . . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
4. Open Issues . . . . . . . . . . . . . . . . . . . . . . . . . 4
|
||||
5. New features in CalDAV . . . . . . . . . . . . . . . . . . . . 4
|
||||
5.1. Proxy Principal Resource . . . . . . . . . . . . . . . . . 4
|
||||
5.2. Privilege Provisioning . . . . . . . . . . . . . . . . . . 8
|
||||
6. Security Considerations . . . . . . . . . . . . . . . . . . . 9
|
||||
7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 9
|
||||
8. Normative References . . . . . . . . . . . . . . . . . . . . . 9
|
||||
Appendix A. Acknowledgments . . . . . . . . . . . . . . . . . . . 9
|
||||
Appendix B. Change History . . . . . . . . . . . . . . . . . . . 10
|
||||
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 10
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 1]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
CalDAV [RFC4791] provides a way for calendar users to store calendar
|
||||
data and exchange this data via scheduling operations. Based on the
|
||||
WebDAV protocol [RFC2518], it also includes the ability to manage
|
||||
access to calendar data via the WebDAV ACL extension [RFC3744].
|
||||
|
||||
It is often common for a calendar user to delegate some form of
|
||||
responsibility for their calendar and schedules to another calendar
|
||||
user (e.g., a boss allows an assistant to check a calendar or to send
|
||||
and accept scheduling invites on his behalf). The user handling the
|
||||
calendar data on behalf of someone else is often referred to as a
|
||||
"calendar user proxy".
|
||||
|
||||
Whilst CalDAV does have fine-grained access control features that can
|
||||
be used to setup complex sharing and management of calendars, often
|
||||
the proxy behavior required is an "all-or-nothing" approach - i.e.
|
||||
the proxy has access to all the calendars or to no calendars (in
|
||||
which case they are of course not a proxy). So a simple way to
|
||||
manage access to an entire set of calendars and scheduling ability
|
||||
would be handy.
|
||||
|
||||
In addition, calendar user agents will often want to display to a
|
||||
user who has proxy access to their calendars, or to whom they are
|
||||
acting as a proxy. Again, CalDAV's access control discovery and
|
||||
report features can be used to do that, but with fine-grained control
|
||||
that exists, it can be hard to tell who is a "real" proxy as opposed
|
||||
to someone just granted rights to some subset of calendars. Again, a
|
||||
simple way to discover proxy information would be handy.
|
||||
|
||||
|
||||
2. Conventions Used in This Document
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
When XML element types in the namespace "DAV:" are referenced in this
|
||||
document outside of the context of an XML fragment, the string "DAV:"
|
||||
will be prefixed to the element type names.
|
||||
|
||||
When XML element types in the namespaces "DAV:" and
|
||||
"urn:ietf:params:xml:ns:caldav" are referenced in this document
|
||||
outside of the context of an XML fragment, the string "DAV:" and
|
||||
"CALDAV:" will be prefixed to the element type names respectively.
|
||||
|
||||
The namespace "http://calendarserver.org/ns/" is used for XML
|
||||
elements defined in this specification. When XML element types in
|
||||
|
||||
|
||||
|
||||
Daboo [Page 2]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
this namespace are referenced in this document outside of the context
|
||||
of an XML fragment, the string "CS:" will be prefixed to the element
|
||||
type names respectively.
|
||||
|
||||
|
||||
3. Overview
|
||||
|
||||
3.1. Server
|
||||
|
||||
For each calendar user principal on the server, the server will
|
||||
generate two group principals - "proxy groups". One is used to hold
|
||||
the list of principals who have read-only proxy access to the main
|
||||
principal's calendars, the other holds the list of principals who
|
||||
have read-write and scheduling proxy access. NB these new group
|
||||
principals would have no equivalent in Open Directory.
|
||||
|
||||
Privileges on each "proxy group" principal will be set so that the
|
||||
"owner" has the ability to change property values.
|
||||
|
||||
The "proxy group" principals will be child resources of the user
|
||||
principal resource with specific resource types and thus are easy to
|
||||
discover. As a result the user principal resources will also be
|
||||
collection resources.
|
||||
|
||||
When provisioning the calendar user home collection, the server will:
|
||||
|
||||
a. Add an ACE to the calendar home collection giving the read-only
|
||||
"proxy group" inheritable read access.
|
||||
|
||||
b. Add an ACE to the calendar home collection giving the read-write
|
||||
"proxy group" inheritable read-write access.
|
||||
|
||||
c. Add an ACE to each of the calendar Inbox and Outbox collections
|
||||
giving the CALDAV:schedule privilege
|
||||
[I-D.desruisseaux-caldav-sched] to the read-write "proxy group".
|
||||
|
||||
3.2. Client
|
||||
|
||||
A client can see who the proxies are for the current principal by
|
||||
examining the principal resource for the two "proxy group" properties
|
||||
and then looking at the DAV:group-member-set property of each.
|
||||
|
||||
The client can edit the list of proxies for the current principal by
|
||||
editing the DAV:group-member-set property on the relevant "proxy
|
||||
group" principal resource.
|
||||
|
||||
The client can find out who the current principal is a proxy for by
|
||||
running a DAV:principal-match REPORT on the principal collection.
|
||||
|
||||
|
||||
|
||||
Daboo [Page 3]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
Alternatively, the client can find out who the current principal is a
|
||||
proxy for by examining the DAV:group-membership property on the
|
||||
current principal resource looking for membership in other users'
|
||||
"proxy groups".
|
||||
|
||||
|
||||
4. Open Issues
|
||||
|
||||
1. Do we want to separate read-write access to calendars vs the
|
||||
ability to schedule as a proxy?
|
||||
|
||||
2. We may want to restrict changing properties on the proxy group
|
||||
collections to just the DAV:group-member-set property?
|
||||
|
||||
3. There is no way for a proxy to be able to manage the list of
|
||||
proxies. We could allow the main calendar user DAV:write-acl on
|
||||
their "proxy group" principals, in which case they could grant
|
||||
others the right to modify the group membership.
|
||||
|
||||
4. Should the "proxy group" principals also be collections given
|
||||
that the regular principal resources will be?
|
||||
|
||||
|
||||
5. New features in CalDAV
|
||||
|
||||
5.1. Proxy Principal Resource
|
||||
|
||||
Each "regular" principal resource that needs to allow calendar user
|
||||
proxy support MUST be a collection resource. i.e. in addition to
|
||||
including the DAV:principal XML element in the DAV:resourcetype
|
||||
property on the resource, it MUST also include the DAV:collection XML
|
||||
element.
|
||||
|
||||
Each "regular" principal resource MUST contain two child resources
|
||||
with names "calendar-proxy-read" and "calendar-proxy-write" (note
|
||||
that these are only suggested names - the server could choose any
|
||||
unique name for these). These resources are themselves principal
|
||||
resources that are groups contain the list of principals for calendar
|
||||
users who can act as a read-only or read-write proxy respectively.
|
||||
|
||||
The server MUST include the CS:calendar-proxy-read or CS:calendar-
|
||||
proxy-write XML elements in the DAV:resourcetype property of the
|
||||
child resources, respectively. This allows clients to discover the
|
||||
"proxy group" principals by using a PROPFIND, Depth:1 request on the
|
||||
current user's principal resource and requesting the DAV:resourcetype
|
||||
property be returned. The element type declarations are:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 4]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
<!ELEMENT calendar-proxy-read EMPTY>
|
||||
|
||||
<!ELEMENT calendar-proxy-write EMPTY>
|
||||
|
||||
The server MUST allow the "parent" principal to change the DAV:group-
|
||||
member-set property on each of the "child" "proxy group" principal
|
||||
resources. When a principal is listed as a member of the "child"
|
||||
resource, the server MUST include the "child" resource URI in the
|
||||
DAV:group-membership property on the included principal resource.
|
||||
Note that this is just "normal" behavior for a group principal.
|
||||
|
||||
An example principal resource layout might be:
|
||||
|
||||
+ /
|
||||
+ principals/
|
||||
+ users/
|
||||
+ cyrus/
|
||||
calendar-proxy-read
|
||||
calendar-proxy-write
|
||||
+ red/
|
||||
calendar-proxy-read
|
||||
calendar-proxy-write
|
||||
+ wilfredo/
|
||||
calendar-proxy-read
|
||||
calendar-proxy-write
|
||||
|
||||
If the principal "cyrus" wishes to have the principal "red" act as a
|
||||
calendar user proxy on his behalf and have the ability to change
|
||||
items on his calendar or schedule meetings on his behalf, then he
|
||||
would add the principal resource URI for "red" to the DAV:group-
|
||||
member-set property of the principal resource /principals/users/
|
||||
cyrus/calendar-proxy-write, giving:
|
||||
|
||||
<DAV:group-member-set>
|
||||
<DAV:href>/principals/users/red/</DAV:href>
|
||||
</DAV:group-member-set>
|
||||
|
||||
The DAV:group-membership property on the resource /principals/users/
|
||||
red/ would be:
|
||||
|
||||
<DAV:group-membership>
|
||||
<DAV:href>/principals/users/cyrus/calendar-proxy-write</DAV:href>
|
||||
</DAV:group-membership>
|
||||
|
||||
If the principal "red" was also a read-only proxy for the principal
|
||||
"wilfredo", then the DA:group-membership property on the resource
|
||||
/principals/users/red/ would be:
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 5]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
<DAV:group-membership>
|
||||
<DAV:href>/principals/users/cyrus/calendar-proxy-write</DAV:href>
|
||||
<DAV:href>/principals/users/wilfredo/calendar-proxy-read</DAV:href>
|
||||
</DAV:group-membership>
|
||||
|
||||
Thus a client can discover to which principals a particular principal
|
||||
is acting as a calendar user proxy for by examining the DAV:group-
|
||||
membership property.
|
||||
|
||||
An alternative to discovering which principals a user can proxy as is
|
||||
to use the WebDAV ACL principal-match report, targeted at the
|
||||
principal collections available on the server.
|
||||
|
||||
Example:
|
||||
|
||||
>> Request <<
|
||||
|
||||
REPORT /principals/ HTTP/1.1
|
||||
Host: cal.example.com
|
||||
Depth: 0
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
Authorization: Digest username="red",
|
||||
realm="cal.example.com", nonce="...",
|
||||
uri="/principals/", response="...", opaque="..."
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:principal-match xmlns:D="DAV:">
|
||||
<D:self/>
|
||||
<D:prop>
|
||||
<D:resourcetype/>
|
||||
</D:prop>
|
||||
</D:principal-match>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 6]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
>> Response <<
|
||||
|
||||
HTTP/1.1 207 Multi-Status
|
||||
Date: Fri, 10 Nov 2006 09:32:12 GMT
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:multistatus xmlns:D="DAV:"
|
||||
xmlns:A="http://calendarserver.org/ns/">
|
||||
<D:response>
|
||||
<D:href>/principals/users/red/</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:resourcetype>
|
||||
<D:principal/>
|
||||
<D:collection/>
|
||||
</D:resourcetype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/principals/users/cyrus/calendar-proxy-write</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:resourcetype>
|
||||
<D:principal/>
|
||||
<A:calendar-proxy-write/>
|
||||
</D:resourcetype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
<D:response>
|
||||
<D:href>/principals/users/wilfredo/calendar-proxy-read</D:href>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:resourcetype>
|
||||
<D:principal/>
|
||||
<A:calendar-proxy-read/>
|
||||
</D:resourcetype>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:response>
|
||||
</D:multistatus>
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 7]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
5.2. Privilege Provisioning
|
||||
|
||||
In order for a calendar user proxy to be able to access the calendars
|
||||
of the user they are proxying for the server MUST ensure that the
|
||||
privileges on the relevant calendars are setup accordingly:
|
||||
|
||||
The DAV:read privilege MUST be granted for read-only and read-
|
||||
write calendar user proxy principals
|
||||
|
||||
The DAV:write privilege MUST be granted for read-write calendar
|
||||
user proxy principals.
|
||||
|
||||
Additionally, the CalDAV scheduling Inbox and Outbox calendar
|
||||
collections for the user allowing proxy access, MUST have the CALDAV:
|
||||
schedule privilege [I-D.desruisseaux-caldav-sched] granted for read-
|
||||
write calendar user proxy principals.
|
||||
|
||||
Note that with a suitable repository layout, a server may be able to
|
||||
grant the appropriate privileges on a parent collection and ensure
|
||||
that all the contained collections and resources inherit that. For
|
||||
example, given the following repository layout:
|
||||
|
||||
+ /
|
||||
+ calendars/
|
||||
+ users/
|
||||
+ cyrus/
|
||||
inbox
|
||||
outbox
|
||||
home
|
||||
work
|
||||
+ red/
|
||||
inbox
|
||||
outbox
|
||||
work
|
||||
soccer
|
||||
+ wilfredo/
|
||||
inbox
|
||||
outbox
|
||||
home
|
||||
work
|
||||
flying
|
||||
|
||||
In order for the principal "red" to act as a read-write proxy for the
|
||||
principal "cyrus", the following WebDAV ACE will need to be granted
|
||||
on the resource /calendars/users/cyrus/ and all children of that
|
||||
resource:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 8]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
<DAV:ace>
|
||||
<DAV:principal>
|
||||
<DAV:href>/principals/users/cyrus/calendar-proxy-write</DAV:href>
|
||||
</DAV:principal>
|
||||
<DAV:privileges>
|
||||
<DAV:grant><DAV:read/><DAV:write/></DAV:grant>
|
||||
</DAV:privileges>
|
||||
</DAV:ace>
|
||||
|
||||
|
||||
6. Security Considerations
|
||||
|
||||
TBD
|
||||
|
||||
|
||||
7. IANA Considerations
|
||||
|
||||
This document does not require any actions on the part of IANA.
|
||||
|
||||
|
||||
8. Normative References
|
||||
|
||||
[I-D.desruisseaux-caldav-sched]
|
||||
Desruisseaux, B., "Scheduling Extensions to CalDAV",
|
||||
draft-desruisseaux-caldav-sched-03 (work in progress),
|
||||
January 2007.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, S., and D.
|
||||
Jensen, "HTTP Extensions for Distributed Authoring --
|
||||
WEBDAV", RFC 2518, February 1999.
|
||||
|
||||
[RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web
|
||||
Distributed Authoring and Versioning (WebDAV) Access
|
||||
Control Protocol", RFC 3744, May 2004.
|
||||
|
||||
[RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault,
|
||||
"Calendaring Extensions to WebDAV (CalDAV)", RFC 4791,
|
||||
March 2007.
|
||||
|
||||
|
||||
Appendix A. Acknowledgments
|
||||
|
||||
This specification is the result of discussions between the Apple
|
||||
calendar server and client teams.
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 9]
|
||||
|
||||
CalDAV Proxy May 2007
|
||||
|
||||
|
||||
Appendix B. Change History
|
||||
|
||||
Changes from -00:
|
||||
|
||||
1. Updated to RFC 4791 reference.
|
||||
|
||||
Changes from -00:
|
||||
|
||||
1. Added more details on actual CalDAV protocol changes.
|
||||
|
||||
2. Changed namespace from http://apple.com/ns/calendarserver/ to
|
||||
http://calendarserver.org/ns/.
|
||||
|
||||
3. Made "proxy group" principals child resources of their "owner"
|
||||
principals.
|
||||
|
||||
4. The "proxy group" principals now have their own resourcetype.
|
||||
|
||||
|
||||
Author's Address
|
||||
|
||||
Cyrus Daboo
|
||||
Apple Computer, Inc.
|
||||
1 Infinite Loop
|
||||
Cupertino, CA 95014
|
||||
USA
|
||||
|
||||
Email: cyrus@daboo.name
|
||||
URI: http://www.apple.com/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo [Page 10]
|
||||
|
560
dav/SabreDAV/docs/draft-daboo-carddav-directory-gateway-02.txt
Normal file
560
dav/SabreDAV/docs/draft-daboo-carddav-directory-gateway-02.txt
Normal file
|
@ -0,0 +1,560 @@
|
|||
|
||||
|
||||
|
||||
Network Working Group C. Daboo
|
||||
Internet-Draft Apple Inc.
|
||||
Updates: XXXX-CardDAV August 24, 2010
|
||||
(if approved)
|
||||
Intended status: Standards Track
|
||||
Expires: February 25, 2011
|
||||
|
||||
|
||||
CardDAV Directory Gateway Extension
|
||||
draft-daboo-carddav-directory-gateway-02
|
||||
|
||||
Abstract
|
||||
|
||||
This document defines an extension to the vCard Extensions to WebDAV
|
||||
(CardDAV) protocol that allows a server to expose a directory as a
|
||||
read-only address book collection.
|
||||
|
||||
Status of this Memo
|
||||
|
||||
This Internet-Draft is submitted in full conformance with the
|
||||
provisions of BCP 78 and BCP 79.
|
||||
|
||||
Internet-Drafts are working documents of the Internet Engineering
|
||||
Task Force (IETF). Note that other groups may also distribute
|
||||
working documents as Internet-Drafts. The list of current Internet-
|
||||
Drafts is at http://datatracker.ietf.org/drafts/current/.
|
||||
|
||||
Internet-Drafts are draft documents valid for a maximum of six months
|
||||
and may be updated, replaced, or obsoleted by other documents at any
|
||||
time. It is inappropriate to use Internet-Drafts as reference
|
||||
material or to cite them other than as "work in progress."
|
||||
|
||||
This Internet-Draft will expire on February 25, 2011.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2010 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 1]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction and Overview . . . . . . . . . . . . . . . . . . 3
|
||||
2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3. CARDDAV:directory-gateway Property . . . . . . . . . . . . . . 4
|
||||
4. XML Element Definitions . . . . . . . . . . . . . . . . . . . 5
|
||||
4.1. CARDDAV:directory . . . . . . . . . . . . . . . . . . . . 5
|
||||
5. Client Guidelines . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
6. Server Guidelines . . . . . . . . . . . . . . . . . . . . . . 6
|
||||
7. Security Considerations . . . . . . . . . . . . . . . . . . . 7
|
||||
8. IANA Consideration . . . . . . . . . . . . . . . . . . . . . . 8
|
||||
9. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 8
|
||||
10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 8
|
||||
10.1. Normative References . . . . . . . . . . . . . . . . . . . 8
|
||||
10.2. Informative References . . . . . . . . . . . . . . . . . . 9
|
||||
Appendix A. Change History (to be removed prior to
|
||||
publication as an RFC) . . . . . . . . . . . . . . . 9
|
||||
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 10
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 2]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
1. Introduction and Overview
|
||||
|
||||
The CardDAV [I-D.ietf-vcarddav-carddav] protocol defines a standard
|
||||
way of accessing, managing, and sharing contact information based on
|
||||
the vCard [RFC2426] format. Often, in an enterprise or service
|
||||
provider environment, a directory of all users hosted on the server
|
||||
(or elsewhere) is available (for example via Lightweight Directory
|
||||
Access Protocol (LDAP) [RFC4510] or some direct database access). It
|
||||
would be convenient for CardDAV clients if this directory were
|
||||
exposed as a "global" address book on the CardDAV server so it could
|
||||
be searched in the same way as personal address books are. This
|
||||
specification defines a "directory gateway" feature extension to
|
||||
CardDAV to enable this.
|
||||
|
||||
This specification adds one new WebDAV property to principal
|
||||
resources that contains the URL to one or more directory gateway
|
||||
address book collection resources. It is important for clients to be
|
||||
able to distinguish this address book collection from others because
|
||||
there are specific limitations involved in using it as described
|
||||
below. To aid that, this specification defines an XML element that
|
||||
can be included as a child element of the DAV:resourcetype property
|
||||
of address book collections to identify them as directory gateways.
|
||||
|
||||
Note that this feature is in no way intended to replace full
|
||||
directory access - it is meant to simply provide a convenient way for
|
||||
CardDAV clients to query contact-related attributes in directory
|
||||
records.
|
||||
|
||||
|
||||
2. Conventions
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
The term "protected" is used in the Conformance field of property
|
||||
definitions as defined in Section 15 of [RFC4918].
|
||||
|
||||
This document uses XML DTD fragments ([W3C.REC-xml-20081126], Section
|
||||
3.2) as a purely notational convention. WebDAV request and response
|
||||
bodies cannot be validated by a DTD due to the specific extensibility
|
||||
rules defined in Section 17 of [RFC4918] and due to the fact that all
|
||||
XML elements defined by this specification use the XML namespace name
|
||||
"DAV:". In particular:
|
||||
|
||||
1. element names use the "DAV:" namespace,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 3]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
2. element ordering is irrelevant unless explicitly stated,
|
||||
|
||||
3. extension elements (elements not already defined as valid child
|
||||
elements) may be added anywhere, except when explicitly stated
|
||||
otherwise,
|
||||
|
||||
4. extension attributes (attributes not already defined as valid for
|
||||
this element) may be added anywhere, except when explicitly
|
||||
stated otherwise.
|
||||
|
||||
When XML element types in the namespaces "DAV:" and
|
||||
"urn:ietf:params:xml:ns:carddav" are referenced in this document
|
||||
outside of the context of an XML fragment, the strings "DAV:" and
|
||||
"CARDDAV:" will be prefixed to the element types, respectively.
|
||||
|
||||
|
||||
3. CARDDAV:directory-gateway Property
|
||||
|
||||
Name: directory-gateway
|
||||
|
||||
Namespace: urn:ietf:params:xml:ns:carddav
|
||||
|
||||
Purpose: Identifies URLs of CardDAV address book collections acting
|
||||
as a directory gateway for the server.
|
||||
|
||||
Protected: MUST be protected.
|
||||
|
||||
allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop
|
||||
request.
|
||||
|
||||
Description: The CARDDAV:directory-gateway identifies address book
|
||||
collection resources that are directory gateway address books for
|
||||
the server.
|
||||
|
||||
Definition:
|
||||
|
||||
<!ELEMENT directory-gateway (DAV:href*)>
|
||||
|
||||
Example:
|
||||
|
||||
<C:directory-gateway xmlns:D="DAV:"
|
||||
xmlns:C="urn:ietf:params:xml:ns:carddav">
|
||||
<D:href>/directory</D:href>
|
||||
</C:directory-gateway>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 4]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
4. XML Element Definitions
|
||||
|
||||
4.1. CARDDAV:directory
|
||||
|
||||
Name: directory
|
||||
|
||||
Namespace: urn:ietf:params:xml:ns:carddav
|
||||
|
||||
Purpose: Used to indicate that an address book collection is a
|
||||
directory gateway.
|
||||
|
||||
Description: This element appears in the DAV:resourcetype property
|
||||
on a address book collection resources that are directory
|
||||
gateways. Clients can use the presence of this element to
|
||||
identify directory gateway collections when doing PROPFINDs to
|
||||
list collection contents.
|
||||
|
||||
Definition:
|
||||
|
||||
<!ELEMENT directory EMPTY>
|
||||
|
||||
Example:
|
||||
|
||||
<D:resourcetype xmlns:D="DAV:"
|
||||
xmlns:C="urn:ietf:params:xml:ns:carddav">
|
||||
<D:collection/>
|
||||
<C:addressbook/>
|
||||
<C:directory/>
|
||||
</D:resourcetype>
|
||||
|
||||
|
||||
5. Client Guidelines
|
||||
|
||||
Clients wishing to make use of directory gateway address books can
|
||||
request the CARDDAV:directory-gateway property (Section 3) when
|
||||
examining other properties on the principal resource for the user.
|
||||
If the property is not present, then the directory gateway feature is
|
||||
not supported by the server at that time.
|
||||
|
||||
Clients can also detect the presence of directory gateway address
|
||||
book collections by retrieving the DAV:resourcetype property on
|
||||
collections that it lists, and look for the presence of the CARDDAV:
|
||||
directory element (Section 4.1).
|
||||
|
||||
Since the directory being exposed via a directory gateway address
|
||||
book collection could be large, clients SHOULD limit the number of
|
||||
results returned in an CARDDAV:addressbook-query REPORT as defined in
|
||||
Section 8.6.1 of [I-D.ietf-vcarddav-carddav].
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 5]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
Clients MUST treat the directory gateway address book collection as a
|
||||
read-only collection, so HTTP methods that modify resource data or
|
||||
properties in the address book collection MUST NOT be used.
|
||||
|
||||
Clients SHOULD NOT attempt to cache the entire contents of the
|
||||
directory gateway address book collection resource by retrieving all
|
||||
resources, or trying to examine all the properties of all resources
|
||||
(e.g., via a PROPFIND Depth:1 request). Instead, CARDDAV:
|
||||
addressbook-query REPORTs are used to search for specific address
|
||||
book object resources, and CARDDAV:multiget REPORTs and individual
|
||||
GET requests can be made to retrieve the actual vCard data for
|
||||
address book object resources found via a query.
|
||||
|
||||
When presenting directory gateway collections to the user, clients
|
||||
SHOULD use the DAV:displayname property on the corresponding address
|
||||
book collections as the name of the directory gateway. This is
|
||||
important in the case where more than one directory gateway is
|
||||
available. Clients MAY also provide descriptive information about
|
||||
each directory gateway by examining the CARDDAV:addressbook-
|
||||
description property (see Section 6.2.1 of
|
||||
[I-D.ietf-vcarddav-carddav]) on the resource.
|
||||
|
||||
|
||||
6. Server Guidelines
|
||||
|
||||
Servers wishing to expose a directory gateway as an address book
|
||||
collection MUST include the CARDDAV:directory-gateway property on all
|
||||
principal resources of users expected to use the feature.
|
||||
|
||||
Since the directory being exposed via the directory gateway address
|
||||
book collection could be large, servers SHOULD truncate the number of
|
||||
results returned in an CARDDAV:addressbook-query REPORT as defined in
|
||||
Section 8.6.2 of [I-D.ietf-vcarddav-carddav]. In addition, servers
|
||||
SHOULD disallow requests that effectively enumerate the collection
|
||||
contents (e.g., PROPFIND Depth:1, trivial CARDDAV:addressbook-query,
|
||||
DAV:sync-collection REPORT).
|
||||
|
||||
Servers need to expose the directory information as a set of address
|
||||
book object resources in the directory gateway address book
|
||||
collection resource. To do that, a mapping between the directory
|
||||
record format and the vCard data has to be applied. In general, only
|
||||
directory record attributes that have a direct equivalent in vCard
|
||||
SHOULD be mapped. It is up to individual implementations to
|
||||
determine which attributes to map. But in all cases servers MUST
|
||||
generate valid vCard data as returned to the client. In addition, as
|
||||
required by CardDAV, the UID vCard property MUST be present in the
|
||||
vCard data, and this value MUST be persistent from query to query for
|
||||
the same directory record.
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 6]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
Multiple directory sources could be available to the server. The
|
||||
server MAY use a single directory gateway resource to aggregate
|
||||
results from each directory source. When doing so care is needed
|
||||
when dealing with potential records that refer to the same entity.
|
||||
Servers MAY suppress any duplicates that they are able to determine
|
||||
themselves. Alternatively, multiple directory sources can be exposed
|
||||
as separate directory gateway resources.
|
||||
|
||||
For any directory source, a server MAY expose multiple directory
|
||||
gateway resources where each represents a different query "scope" for
|
||||
the directory. Different scopes MAY be offered to different
|
||||
principals on the server. For example, the server might expose an
|
||||
entire company directory for searching as the resource "/directory-
|
||||
all" to all principals, but then provide "/directory-department-XYZ"
|
||||
as another directory gateway that has a search scope that implicitly
|
||||
limits the search results to just the "XYZ" department. Users in
|
||||
that department would then have a CARDDAV:directory-gateway property
|
||||
on their principal resource that included the "/directory-department-
|
||||
XYZ" resource. Users in other departments would have corresponding
|
||||
directory gateway resources available to them.
|
||||
|
||||
Records in a directory can include data for more than just people,
|
||||
e.g, resources such as rooms or projectors, groups, computer systems
|
||||
etc. It is up to individual implementations to determine the most
|
||||
appropriate "scope" for the data returned via the directory gateway
|
||||
by filtering the appropriate record types. As above, servers could
|
||||
choose to expose people and resources under different directory
|
||||
gateway resources by implicitly limiting the search "scope" for each
|
||||
of those.
|
||||
|
||||
Servers MAY apply implementation defined access rules to determine,
|
||||
on a per-user basis, what records are returned to a particularly user
|
||||
and the content of those records exposed via vCard data. This per-
|
||||
user behavior is in addition to the general security requirements
|
||||
detailed below.
|
||||
|
||||
When multiple directory gateway collections are present, servers
|
||||
SHOULD provide a DAV:displayname property on each that disambiguates
|
||||
them. Servers MAY include a CARDDAV:addressbook-description property
|
||||
(see Section 6.2.1 of [I-D.ietf-vcarddav-carddav]) on each directory
|
||||
gateway resource to provide a description of the directory and any
|
||||
search "scope" that might be used, or any other useful information
|
||||
for users.
|
||||
|
||||
|
||||
7. Security Considerations
|
||||
|
||||
Servers MUST ensure that client requests against the directory
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 7]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
gateway address book collection cannot use excessive resources (CPU,
|
||||
memory, network bandwidth etc), given that the directory could be
|
||||
large.
|
||||
|
||||
Servers MUST take care not to expose sensitive directory record
|
||||
attributes in the vCard data via the directory gateway address book.
|
||||
In general only those properties that have direct correspondence in
|
||||
vCard SHOULD be exposed.
|
||||
|
||||
Servers need to determine whether it is appropriate for the directory
|
||||
information to be available via CardDAV to unauthenticated users. If
|
||||
not, servers MUST ensure that unauthenticated users do not have
|
||||
access to the directory gateway address book object resource and its
|
||||
contents. If unauthenticated access is allowed, servers MAY choose
|
||||
to limit the set of vCard properties that are searchable or returned
|
||||
in the address book object resources when unauthenticated requests
|
||||
are made.
|
||||
|
||||
|
||||
8. IANA Consideration
|
||||
|
||||
This document does not require any actions on the part of IANA.
|
||||
|
||||
|
||||
9. Acknowledgments
|
||||
|
||||
|
||||
10. References
|
||||
|
||||
10.1. Normative References
|
||||
|
||||
[I-D.ietf-vcarddav-carddav]
|
||||
Daboo, C., "vCard Extensions to WebDAV (CardDAV)",
|
||||
draft-ietf-vcarddav-carddav-10 (work in progress),
|
||||
November 2009.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC2426] Dawson, F. and T. Howes, "vCard MIME Directory Profile",
|
||||
RFC 2426, September 1998.
|
||||
|
||||
[RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed
|
||||
Authoring and Versioning (WebDAV)", RFC 4918, June 2007.
|
||||
|
||||
[W3C.REC-xml-20081126]
|
||||
Paoli, J., Yergeau, F., Bray, T., Sperberg-McQueen, C.,
|
||||
and E. Maler, "Extensible Markup Language (XML) 1.0 (Fifth
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 8]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
Edition)", World Wide Web Consortium Recommendation REC-
|
||||
xml-20081126, November 2008,
|
||||
<http://www.w3.org/TR/2008/REC-xml-20081126>.
|
||||
|
||||
10.2. Informative References
|
||||
|
||||
[RFC4510] Zeilenga, K., "Lightweight Directory Access Protocol
|
||||
(LDAP): Technical Specification Road Map", RFC 4510,
|
||||
June 2006.
|
||||
|
||||
|
||||
Appendix A. Change History (to be removed prior to publication as an
|
||||
RFC)
|
||||
|
||||
Changes in -02
|
||||
|
||||
1. Added CARDDAV:directory element for use in DAV:resourcetype
|
||||
|
||||
2. Allow CARDDAV:directory-gateway to be multi-valued
|
||||
|
||||
3. Explain how a server could implicit "scope" queries on different
|
||||
directory gateway resources
|
||||
|
||||
Changes in -01
|
||||
|
||||
1. Remove duplicated text in a couple of sections
|
||||
|
||||
2. Add example of LDAP/generic database as possible directory
|
||||
"sources"
|
||||
|
||||
3. Add text to explain why the client needs to treat this as special
|
||||
and thus the need for a property
|
||||
|
||||
4. Added text to server guidelines indicating requirements for
|
||||
handling vCard UID properties
|
||||
|
||||
5. Added text to server guidelines explain that different record
|
||||
"types" may exist in the directory and the server is free to
|
||||
filter those as appropriate
|
||||
|
||||
6. Added text to server guidelines indicating that server are free
|
||||
to aggregate directory records from multiple sources
|
||||
|
||||
7. Added text to server guidelines indicating that servers are free
|
||||
to apply implementation defined access control to the returned
|
||||
data on a per-user basis
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 9]
|
||||
|
||||
Internet-Draft CardDAV Directory Gateway Extension August 2010
|
||||
|
||||
|
||||
Author's Address
|
||||
|
||||
Cyrus Daboo
|
||||
Apple Inc.
|
||||
1 Infinite Loop
|
||||
Cupertino, CA 95014
|
||||
USA
|
||||
|
||||
Email: cyrus@daboo.name
|
||||
URI: http://www.apple.com/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Expires February 25, 2011 [Page 10]
|
||||
|
5544
dav/SabreDAV/docs/draft-desruisseaux-caldav-sched-10.txt
Normal file
5544
dav/SabreDAV/docs/draft-desruisseaux-caldav-sched-10.txt
Normal file
File diff suppressed because it is too large
Load diff
5152
dav/SabreDAV/docs/draft-ietf-httpbis-p1-messaging-11.txt
Normal file
5152
dav/SabreDAV/docs/draft-ietf-httpbis-p1-messaging-11.txt
Normal file
File diff suppressed because it is too large
Load diff
1512
dav/SabreDAV/docs/draft-ietf-httpbis-p4-conditional-11.txt
Normal file
1512
dav/SabreDAV/docs/draft-ietf-httpbis-p4-conditional-11.txt
Normal file
File diff suppressed because it is too large
Load diff
1512
dav/SabreDAV/docs/draft-ietf-httpbis-p5-range-11.txt
Normal file
1512
dav/SabreDAV/docs/draft-ietf-httpbis-p5-range-11.txt
Normal file
File diff suppressed because it is too large
Load diff
2352
dav/SabreDAV/docs/draft-ietf-httpbis-p6-cache-11.txt
Normal file
2352
dav/SabreDAV/docs/draft-ietf-httpbis-p6-cache-11.txt
Normal file
File diff suppressed because it is too large
Load diff
560
dav/SabreDAV/docs/draft-nottingham-http-new-status-04.txt
Normal file
560
dav/SabreDAV/docs/draft-nottingham-http-new-status-04.txt
Normal file
|
@ -0,0 +1,560 @@
|
|||
|
||||
|
||||
|
||||
Network Working Group M. Nottingham
|
||||
Internet-Draft Rackspace
|
||||
Updates: 2616 (if approved) R. Fielding
|
||||
Intended status: Standards Track Adobe
|
||||
Expires: August 7, 2012 February 4, 2012
|
||||
|
||||
|
||||
Additional HTTP Status Codes
|
||||
draft-nottingham-http-new-status-04
|
||||
|
||||
Abstract
|
||||
|
||||
This document specifies additional HyperText Transfer Protocol (HTTP)
|
||||
status codes for a variety of common situations.
|
||||
|
||||
Editorial Note (To be removed by RFC Editor before publication)
|
||||
|
||||
Distribution of this document is unlimited. Although this is not a
|
||||
work item of the HTTPbis Working Group, comments should be sent to
|
||||
the Hypertext Transfer Protocol (HTTP) mailing list at
|
||||
ietf-http-wg@w3.org [1], which may be joined by sending a message
|
||||
with subject "subscribe" to ietf-http-wg-request@w3.org [2].
|
||||
|
||||
Discussions of the HTTPbis Working Group are archived at
|
||||
<http://lists.w3.org/Archives/Public/ietf-http-wg/>.
|
||||
|
||||
Status of this Memo
|
||||
|
||||
This Internet-Draft is submitted in full conformance with the
|
||||
provisions of BCP 78 and BCP 79.
|
||||
|
||||
Internet-Drafts are working documents of the Internet Engineering
|
||||
Task Force (IETF). Note that other groups may also distribute
|
||||
working documents as Internet-Drafts. The list of current Internet-
|
||||
Drafts is at http://datatracker.ietf.org/drafts/current/.
|
||||
|
||||
Internet-Drafts are draft documents valid for a maximum of six months
|
||||
and may be updated, replaced, or obsoleted by other documents at any
|
||||
time. It is inappropriate to use Internet-Drafts as reference
|
||||
material or to cite them other than as "work in progress."
|
||||
|
||||
This Internet-Draft will expire on August 7, 2012.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2012 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 1]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
2. Requirements . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
3. 428 Precondition Required . . . . . . . . . . . . . . . . . . . 3
|
||||
4. 429 Too Many Requests . . . . . . . . . . . . . . . . . . . . . 4
|
||||
5. 431 Request Header Fields Too Large . . . . . . . . . . . . . . 4
|
||||
6. 511 Network Authentication Required . . . . . . . . . . . . . . 5
|
||||
7. Security Considerations . . . . . . . . . . . . . . . . . . . . 6
|
||||
8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 7
|
||||
9. References . . . . . . . . . . . . . . . . . . . . . . . . . . 8
|
||||
9.1. Normative References . . . . . . . . . . . . . . . . . . . 8
|
||||
9.2. Informative References . . . . . . . . . . . . . . . . . . 8
|
||||
Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . . 8
|
||||
Appendix B. Issues Raised by Captive Portals . . . . . . . . . . . 8
|
||||
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 9
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 2]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
This document specifies additional HTTP [RFC2616] status codes for a
|
||||
variety of common situations, to improve interoperability and avoid
|
||||
confusion when other, less precise status codes are used.
|
||||
|
||||
Note that these status codes are optional; servers cannot be required
|
||||
to support them. However, because clients will treat unknown status
|
||||
codes as a generic error of the same class (e.g., 499 is treated as
|
||||
400 if it is not recognized), they can be safely deployed by existing
|
||||
servers (see [RFC2616] Section 6.1.1 for more information).
|
||||
|
||||
|
||||
2. Requirements
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
|
||||
3. 428 Precondition Required
|
||||
|
||||
The 428 status code indicates that the origin server requires the
|
||||
request to be conditional.
|
||||
|
||||
Its typical use is to avoid the "lost update" problem, where a client
|
||||
GETs a resource's state, modifies it, and PUTs it back to the server,
|
||||
when meanwhile a third party has modified the state on the server,
|
||||
leading to a conflict. By requiring requests to be conditional, the
|
||||
server can assure that clients are working with the correct copies.
|
||||
|
||||
Responses using this status code SHOULD explain how to resubmit the
|
||||
request successfully. For example:
|
||||
|
||||
HTTP/1.1 428 Precondition Required
|
||||
Content-Type: text/html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Precondition Required</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Precondition Required</h1>
|
||||
<p>This request is required to be conditional;
|
||||
try using "If-Match".</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 3]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
Responses with the 428 status code MUST NOT be stored by a cache.
|
||||
|
||||
|
||||
4. 429 Too Many Requests
|
||||
|
||||
The 429 status code indicates that the user has sent too many
|
||||
requests in a given amount of time ("rate limiting").
|
||||
|
||||
The response representations SHOULD include details explaining the
|
||||
condition, and MAY include a Retry-After header indicating how long
|
||||
to wait before making a new request.
|
||||
|
||||
For example:
|
||||
|
||||
HTTP/1.1 429 Too Many Requests
|
||||
Content-Type: text/html
|
||||
Retry-After: 3600
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Too Many Requests</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Too Many Requests</h1>
|
||||
<p>I only allow 50 requests per hour to this Web site per
|
||||
logged in user. Try again soon.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Note that this specification does not define how the origin server
|
||||
identifies the user, nor how it counts requests. For example, an
|
||||
origin server that is limiting request rates can do so based upon
|
||||
counts of requests on a per-resource basis, across the entire server,
|
||||
or even among a set of servers. Likewise, it might identify the user
|
||||
by its authentication credentials, or a stateful cookie.
|
||||
|
||||
Responses with the 429 status code MUST NOT be stored by a cache.
|
||||
|
||||
|
||||
5. 431 Request Header Fields Too Large
|
||||
|
||||
The 431 status code indicates that the server is unwilling to process
|
||||
the request because its header fields are too large. The request MAY
|
||||
be resubmitted after reducing the size of the request header fields.
|
||||
|
||||
It can be used both when the set of request header fields in total
|
||||
are too large, and when a single header field is at fault. In the
|
||||
latter case, the response representation SHOULD specify which header
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 4]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
field was too large.
|
||||
|
||||
For example:
|
||||
|
||||
HTTP/1.1 431 Request Header Fields Too Large
|
||||
Content-Type: text/html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Request Header Fields Too Large</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Request Header Fields Too Large</h1>
|
||||
<p>The "Example" header was too large.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Responses with the 431 status code MUST NOT be stored by a cache.
|
||||
|
||||
|
||||
6. 511 Network Authentication Required
|
||||
|
||||
The 511 status code indicates that the client needs to authenticate
|
||||
to gain network access.
|
||||
|
||||
The response representation SHOULD contain a link to a resource that
|
||||
allows the user to submit credentials (e.g. with a HTML form).
|
||||
|
||||
Note that the 511 response SHOULD NOT contain a challenge or the
|
||||
login interface itself, because browsers would show the login
|
||||
interface as being associated with the originally requested URL,
|
||||
which may cause confusion.
|
||||
|
||||
The 511 status SHOULD NOT be generated by origin servers; it is
|
||||
intended for use by intercepting proxies that are interposed as a
|
||||
means of controlling access to the network.
|
||||
|
||||
Responses with the 511 status code MUST NOT be stored by a cache.
|
||||
|
||||
6.1. The 511 Status Code and Captive Portals
|
||||
|
||||
The 511 status code is designed to mitigate problems caused by
|
||||
"captive portals" to software (especially non-browser agents) that is
|
||||
expecting a response from the server that a request was made to, not
|
||||
the intervening network infrastructure. It is not intended to
|
||||
encouraged deployment of captive portals, only to limit the damage
|
||||
caused by them.
|
||||
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 5]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
A network operator wishing to require some authentication, acceptance
|
||||
of terms or other user interaction before granting access usually
|
||||
does so by identifing clients who have not done so ("unknown
|
||||
clients") using their MAC addresses.
|
||||
|
||||
Unknown clients then have all traffic blocked, except for that on TCP
|
||||
port 80, which is sent to a HTTP server (the "login server")
|
||||
dedicated to "logging in" unknown clients, and of course traffic to
|
||||
the login server itself.
|
||||
|
||||
For example, a user agent might connect to a network and make the
|
||||
following HTTP request on TCP port 80:
|
||||
|
||||
GET /index.htm HTTP/1.1
|
||||
Host: www.example.com
|
||||
|
||||
Upon receiving such a request, the login server would generate a 511
|
||||
response:
|
||||
|
||||
HTTP/1.1 511 Network Authentication Required
|
||||
Content-Type: text/html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Network Authentication Required</title>
|
||||
<meta http-equiv="refresh"
|
||||
content="0; url=https://login.example.net/">
|
||||
</head>
|
||||
<body>
|
||||
<p>You need to <a href="https://login.example.net/">
|
||||
authenticate with the local network</a> in order to gain
|
||||
access.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Here, the 511 status code assures that non-browser clients will not
|
||||
interpret the response as being from the origin server, and the META
|
||||
HTML element redirects the user agent to the login server.
|
||||
|
||||
|
||||
7. Security Considerations
|
||||
|
||||
7.1. 428 Precondition Required
|
||||
|
||||
The 428 status code is optional; clients cannot rely upon its use to
|
||||
prevent "lost update" conflicts.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 6]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
7.2. 429 Too Many Requests
|
||||
|
||||
When a server is under attack or just receiving a very large number
|
||||
of requests from a single party, responding to each with a 429 status
|
||||
code will consume resources.
|
||||
|
||||
Therefore, servers are not required to use the 429 status code; when
|
||||
limiting resource usage, it may be more appropriate to just drop
|
||||
connections, or take other steps.
|
||||
|
||||
7.3. 431 Request Header Fields Too Large
|
||||
|
||||
Servers are not required to use the 431 status code; when under
|
||||
attack, it may be more appropriate to just drop connections, or take
|
||||
other steps.
|
||||
|
||||
7.4. 511 Network Authentication Required
|
||||
|
||||
In common use, a response carrying the 511 status code will not come
|
||||
from the origin server indicated in the request's URL. This presents
|
||||
many security issues; e.g., an attacking intermediary may be
|
||||
inserting cookies into the original domain's name space, may be
|
||||
observing cookies or HTTP authentication credentials sent from the
|
||||
user agent, and so on.
|
||||
|
||||
However, these risks are not unique to the 511 status code; in other
|
||||
words, a captive portal that is not using this status code introduces
|
||||
the same issues.
|
||||
|
||||
Also, note that captive portals using this status code on an SSL or
|
||||
TLS connection (commonly, port 443) will generate a certificate error
|
||||
on the client.
|
||||
|
||||
|
||||
8. IANA Considerations
|
||||
|
||||
The HTTP Status Codes Registry should be updated with the following
|
||||
entries:
|
||||
|
||||
o Code: 428
|
||||
o Description: Precondition Required
|
||||
o Specification: [ this document ]
|
||||
|
||||
o Code: 429
|
||||
o Description: Too Many Requests
|
||||
o Specification: [ this document ]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 7]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
o Code: 431
|
||||
o Description: Request Header Fields Too Large
|
||||
o Specification: [ this document ]
|
||||
|
||||
o Code: 511
|
||||
o Description: Network Authentication Required
|
||||
o Specification: [ this document ]
|
||||
|
||||
|
||||
9. References
|
||||
|
||||
9.1. Normative References
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
|
||||
Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
|
||||
Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
|
||||
|
||||
9.2. Informative References
|
||||
|
||||
[RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault,
|
||||
"Calendaring Extensions to WebDAV (CalDAV)", RFC 4791,
|
||||
March 2007.
|
||||
|
||||
[RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed
|
||||
Authoring and Versioning (WebDAV)", RFC 4918, June 2007.
|
||||
|
||||
URIs
|
||||
|
||||
[1] <mailto:ietf-http-wg@w3.org>
|
||||
|
||||
[2] <mailto:ietf-http-wg-request@w3.org?subject=subscribe>
|
||||
|
||||
|
||||
Appendix A. Acknowledgements
|
||||
|
||||
Thanks to Jan Algermissen and Julian Reschke for their suggestions
|
||||
and feedback.
|
||||
|
||||
|
||||
Appendix B. Issues Raised by Captive Portals
|
||||
|
||||
Since clients cannot differentiate between a portal's response and
|
||||
that of the HTTP server that they intended to communicate with, a
|
||||
number of issues arise. The 511 status code is intended to help
|
||||
mitigate some of them.
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 8]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
One example is the "favicon.ico"
|
||||
<http://en.wikipedia.org/wiki/Favicon> commonly used by browsers to
|
||||
identify the site being accessed. If the favicon for a given site is
|
||||
fetched from a captive portal instead of the intended site (e.g.,
|
||||
because the user is unauthenticated), it will often "stick" in the
|
||||
browser's cache (most implementations cache favicons aggressively)
|
||||
beyond the portal session, so that it seems as if the portal's
|
||||
favicon has "taken over" the legitimate site.
|
||||
|
||||
Another browser-based issue comes about when P3P
|
||||
<http://www.w3.org/TR/P3P/> is supported. Depending on how it is
|
||||
implemented, it's possible a browser might interpret a portal's
|
||||
response for the p3p.xml file as the server's, resulting in the
|
||||
privacy policy (or lack thereof) advertised by the portal being
|
||||
interpreted as applying to the intended site. Other Web-based
|
||||
protocols such as WebFinger
|
||||
<http://code.google.com/p/webfinger/wiki/WebFingerProtocol>, CORS
|
||||
<http://www.w3.org/TR/cors/> and OAuth
|
||||
<http://tools.ietf.org/html/draft-ietf-oauth-v2> may also be
|
||||
vulnerable to such issues.
|
||||
|
||||
Although HTTP is most widely used with Web browsers, a growing number
|
||||
of non-browsing applications use it as a substrate protocol. For
|
||||
example, WebDAV [RFC4918] and CalDAV [RFC4791] both use HTTP as the
|
||||
basis (for remote authoring and calendaring, respectively). Using
|
||||
these applications from behind a captive portal can result in
|
||||
spurious errors being presented to the user, and might result in
|
||||
content corruption, in extreme cases.
|
||||
|
||||
Similarly, other non-browser applications using HTTP can be affected
|
||||
as well; e.g., widgets <http://www.w3.org/TR/widgets/>, software
|
||||
updates, and other specialised software such as Twitter clients and
|
||||
the iTunes Music Store.
|
||||
|
||||
It should be noted that it's sometimes believed that using HTTP
|
||||
redirection to direct traffic to the portal addresses these issues.
|
||||
However, since many of these uses "follow" redirects, this is not a
|
||||
good solution.
|
||||
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Mark Nottingham
|
||||
Rackspace
|
||||
|
||||
Email: mnot@mnot.net
|
||||
URI: http://www.mnot.net/
|
||||
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 9]
|
||||
|
||||
Internet-Draft Additional HTTP Status Codes February 2012
|
||||
|
||||
|
||||
Roy T. Fielding
|
||||
Adobe Systems Incorporated
|
||||
345 Park Ave
|
||||
San Jose, CA 95110
|
||||
USA
|
||||
|
||||
Email: fielding@gbiv.com
|
||||
URI: http://roy.gbiv.com/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Nottingham & Fielding Expires August 7, 2012 [Page 10]
|
||||
|
1851
dav/SabreDAV/docs/rfc2425.txt
Normal file
1851
dav/SabreDAV/docs/rfc2425.txt
Normal file
File diff suppressed because it is too large
Load diff
2355
dav/SabreDAV/docs/rfc2426.txt
Normal file
2355
dav/SabreDAV/docs/rfc2426.txt
Normal file
File diff suppressed because it is too large
Load diff
5267
dav/SabreDAV/docs/rfc2518.txt
Normal file
5267
dav/SabreDAV/docs/rfc2518.txt
Normal file
File diff suppressed because it is too large
Load diff
9859
dav/SabreDAV/docs/rfc2616.txt
Normal file
9859
dav/SabreDAV/docs/rfc2616.txt
Normal file
File diff suppressed because it is too large
Load diff
1907
dav/SabreDAV/docs/rfc2617.txt
Normal file
1907
dav/SabreDAV/docs/rfc2617.txt
Normal file
File diff suppressed because it is too large
Load diff
10329
dav/SabreDAV/docs/rfc3253.pdf
Normal file
10329
dav/SabreDAV/docs/rfc3253.pdf
Normal file
File diff suppressed because one or more lines are too long
6295
dav/SabreDAV/docs/rfc3744.pdf
Normal file
6295
dav/SabreDAV/docs/rfc3744.pdf
Normal file
File diff suppressed because one or more lines are too long
3127
dav/SabreDAV/docs/rfc4437.pdf
Normal file
3127
dav/SabreDAV/docs/rfc4437.pdf
Normal file
File diff suppressed because one or more lines are too long
1459
dav/SabreDAV/docs/rfc4790.txt
Normal file
1459
dav/SabreDAV/docs/rfc4790.txt
Normal file
File diff suppressed because it is too large
Load diff
5995
dav/SabreDAV/docs/rfc4791.txt
Normal file
5995
dav/SabreDAV/docs/rfc4791.txt
Normal file
File diff suppressed because it is too large
Load diff
13609
dav/SabreDAV/docs/rfc4918.pdf
Normal file
13609
dav/SabreDAV/docs/rfc4918.pdf
Normal file
File diff suppressed because one or more lines are too long
395
dav/SabreDAV/docs/rfc5051.txt
Normal file
395
dav/SabreDAV/docs/rfc5051.txt
Normal file
|
@ -0,0 +1,395 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group M. Crispin
|
||||
Request for Comments: 5051 University of Washington
|
||||
Category: Standards Track October 2007
|
||||
|
||||
|
||||
i;unicode-casemap - Simple Unicode Collation Algorithm
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This document specifies an Internet standards track protocol for the
|
||||
Internet community, and requests discussion and suggestions for
|
||||
improvements. Please refer to the current edition of the "Internet
|
||||
Official Protocol Standards" (STD 1) for the standardization state
|
||||
and status of this protocol. Distribution of this memo is unlimited.
|
||||
|
||||
Abstract
|
||||
|
||||
This document describes "i;unicode-casemap", a simple case-
|
||||
insensitive collation for Unicode strings. It provides equality,
|
||||
substring, and ordering operations.
|
||||
|
||||
1. Introduction
|
||||
|
||||
The "i;ascii-casemap" collation described in [COMPARATOR] is quite
|
||||
simple to implement and provides case-independent comparisons for the
|
||||
26 Latin alphabetics. It is specified as the default and/or baseline
|
||||
comparator in some application protocols, e.g., [IMAP-SORT].
|
||||
|
||||
However, the "i;ascii-casemap" collation does not produce
|
||||
satisfactory results with non-ASCII characters. It is possible, with
|
||||
a modest extension, to provide a more sophisticated collation with
|
||||
greater multilingual applicability than "i;ascii-casemap". This
|
||||
extension provides case-independent comparisons for a much greater
|
||||
number of characters. It also collates characters with diacriticals
|
||||
with the non-diacritical character forms.
|
||||
|
||||
This collation, "i;unicode-casemap", is intended to be an alternative
|
||||
to, and preferred over, "i;ascii-casemap". It does not replace the
|
||||
"i;basic" collation described in [BASIC].
|
||||
|
||||
2. Unicode Casemap Collation Description
|
||||
|
||||
The "i;unicode-casemap" collation is a simple collation which is
|
||||
case-insensitive in its treatment of characters. It provides
|
||||
equality, substring, and ordering operations. The validity test
|
||||
operation returns "valid" for any input.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crispin Standards Track [Page 1]
|
||||
|
||||
RFC 5051 i;unicode-casemap October 2007
|
||||
|
||||
|
||||
This collation allows strings in arbitrary (and mixed) character
|
||||
sets, as long as the character set for each string is identified and
|
||||
it is possible to convert the string to Unicode. Strings which have
|
||||
an unidentified character set and/or cannot be converted to Unicode
|
||||
are not rejected, but are treated as binary.
|
||||
|
||||
Each input string is prepared by converting it to a "titlecased
|
||||
canonicalized UTF-8" string according to the following steps, using
|
||||
UnicodeData.txt ([UNICODE-DATA]):
|
||||
|
||||
(1) A Unicode codepoint is obtained from the input string.
|
||||
|
||||
(a) If the input string is in a known charset that can be
|
||||
converted to Unicode, a sequence in the string's charset
|
||||
is read and checked for validity according to the rules of
|
||||
that charset. If the sequence is valid, it is converted
|
||||
to a Unicode codepoint. Note that for input strings in
|
||||
UTF-8, the UTF-8 sequence must be valid according to the
|
||||
rules of [UTF-8]; e.g., overlong UTF-8 sequences are
|
||||
invalid.
|
||||
|
||||
(b) If the input string is in an unknown charset, or an
|
||||
invalid sequence occurs in step (1)(a), conversion ceases.
|
||||
No further preparation is performed, and any partial
|
||||
preparation results are discarded. The original string is
|
||||
used unchanged with the i;octet comparator.
|
||||
|
||||
(2) The following steps, using UnicodeData.txt ([UNICODE-DATA]),
|
||||
are performed on the resulting codepoint from step (1)(a).
|
||||
|
||||
(a) If the codepoint has a titlecase property in
|
||||
UnicodeData.txt (this is normally the same as the
|
||||
uppercase property), the codepoint is converted to the
|
||||
codepoints in the titlecase property.
|
||||
|
||||
(b) If the resulting codepoint from (2)(a) has a decomposition
|
||||
property of any type in UnicodeData.txt, the codepoint is
|
||||
converted to the codepoints in the decomposition property.
|
||||
This step is recursively applied to each of the resulting
|
||||
codepoints until no more decomposition is possible
|
||||
(effectively Normalization Form KD).
|
||||
|
||||
Example: codepoint U+01C4 (LATIN CAPITAL LETTER DZ WITH CARON)
|
||||
has a titlecase property of U+01C5 (LATIN CAPITAL LETTER D
|
||||
WITH SMALL LETTER Z WITH CARON). Codepoint U+01C5 has a
|
||||
decomposition property of U+0044 (LATIN CAPITAL LETTER D)
|
||||
U+017E (LATIN SMALL LETTER Z WITH CARON). U+017E has a
|
||||
decomposition property of U+007A (LATIN SMALL LETTER Z) U+030c
|
||||
|
||||
|
||||
|
||||
Crispin Standards Track [Page 2]
|
||||
|
||||
RFC 5051 i;unicode-casemap October 2007
|
||||
|
||||
|
||||
(COMBINING CARON). Neither U+0044, U+007A, nor U+030C have
|
||||
any decomposition properties. Therefore, U+01C4 is converted
|
||||
to U+0044 U+007A U+030C by this step.
|
||||
|
||||
(3) The resulting codepoint(s) from step (2) is/are appended, in
|
||||
UTF-8 format, to the "titlecased canonicalized UTF-8" string.
|
||||
|
||||
(4) Repeat from step (1) until there is no more data in the input
|
||||
string.
|
||||
|
||||
Following the above preparation process on each string, the equality,
|
||||
ordering, and substring operations are as for i;octet.
|
||||
|
||||
It is permitted to use an alternative implementation of the above
|
||||
preparation process if it produces the same results. For example, it
|
||||
may be more convenient for an implementation to convert all input
|
||||
strings to a sequence of UTF-16 or UTF-32 values prior to performing
|
||||
any of the step (2) actions. Similarly, if all input strings are (or
|
||||
are convertible to) Unicode, it may be possible to use UTF-32 as an
|
||||
alternative to UTF-8 in step (3).
|
||||
|
||||
Note: UTF-16 is unsuitable as an alternative to UTF-8 in step (3),
|
||||
because UTF-16 surrogates will cause i;octet to collate codepoints
|
||||
U+E0000 through U+FFFF after non-BMP codepoints.
|
||||
|
||||
This collation is not locale sensitive. Consequently, care should be
|
||||
taken when using OS-supplied functions to implement this collation.
|
||||
Functions such as strcasecmp and toupper are sometimes locale
|
||||
sensitive and may inconsistently casemap letters.
|
||||
|
||||
The i;unicode-casemap collation is well suited to use with many
|
||||
Internet protocols and computer languages. Use with natural language
|
||||
is often inappropriate; even though the collation apparently supports
|
||||
languages such as Swahili and English, in real-world use it tends to
|
||||
mis-sort a number of types of string:
|
||||
|
||||
o people and place names containing scripts that are not collated
|
||||
according to "alphabetical order".
|
||||
o words with characters that have diacriticals. However,
|
||||
i;unicode-casemap generally does a better job than i;ascii-casemap
|
||||
for most (but not all) languages. For example, German umlaut
|
||||
letters will sort correctly, but some Scandinavian letters will
|
||||
not.
|
||||
o names such as "Lloyd" (which in Welsh sorts after "Lyon", unlike
|
||||
in English),
|
||||
o strings containing other non-letter symbols; e.g., euro and pound
|
||||
sterling symbols, quotation marks other than '"', dashes/hyphens,
|
||||
etc.
|
||||
|
||||
|
||||
|
||||
Crispin Standards Track [Page 3]
|
||||
|
||||
RFC 5051 i;unicode-casemap October 2007
|
||||
|
||||
|
||||
3. Unicode Casemap Collation Registration
|
||||
|
||||
<?xml version='1.0'?>
|
||||
<!DOCTYPE collation SYSTEM 'collationreg.dtd'>
|
||||
<collation rfc="5051" scope="global" intendedUse="common">
|
||||
<identifier>i;unicode-casemap</identifier>
|
||||
<title>Unicode Casemap</title>
|
||||
<operations>equality order substring</operations>
|
||||
<specification>RFC 5051</specification>
|
||||
<owner>IETF</owner>
|
||||
<submitter>mrc@cac.washington.edu</submitter>
|
||||
</collation>
|
||||
|
||||
4. Security Considerations
|
||||
|
||||
The security considerations for [UTF-8], [STRINGPREP], and [UNICODE-
|
||||
SECURITY] apply and are normative to this specification.
|
||||
|
||||
The results from this comparator will vary depending upon the
|
||||
implementation for several reasons. Implementations MUST consider
|
||||
whether these possibilities are a problem for their use case:
|
||||
|
||||
1) New characters added in Unicode may have decomposition or
|
||||
titlecase properties that will not be known to an implementation
|
||||
based upon an older revision of Unicode. This impacts step (2).
|
||||
|
||||
2) Step (2)(b) defines a subset of Normalization Form KD (NFKD) that
|
||||
does not require normalization of out-of-order diacriticals.
|
||||
However, an implementation MAY use an NFKD library routine that
|
||||
does such normalization. This impacts step (2)(b) and possibly
|
||||
also step (1)(a), and is an issue only with ill-formed UTF-8
|
||||
input.
|
||||
|
||||
3) The set of charsets handled in step (1)(a) is open-ended. UTF-8
|
||||
(and, by extension, US-ASCII) are the only mandatory-to-implement
|
||||
charsets. This impacts step (1)(a).
|
||||
|
||||
Implementations SHOULD, as far as feasible, support all the
|
||||
charsets they are likely to encounter in the input data, in order
|
||||
to avoid poor collation caused by the fall through to the (1)(b)
|
||||
rule.
|
||||
|
||||
4) Other charsets may have revisions which add new characters that
|
||||
are not known to an implementation based upon an older revision.
|
||||
This impacts step (1)(a) and possibly also step (1)(b).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crispin Standards Track [Page 4]
|
||||
|
||||
RFC 5051 i;unicode-casemap October 2007
|
||||
|
||||
|
||||
An attacker may create input that is ill-formed or in an unknown
|
||||
charset, with the intention of impacting the results of this
|
||||
comparator or exploiting other parts of the system which process this
|
||||
input in different ways. Note, however, that even well-formed data
|
||||
in a known charset can impact the result of this comparator in
|
||||
unexpected ways. For example, an attacker can substitute U+0041
|
||||
(LATIN CAPITAL LETTER A) with U+0391 (GREEK CAPITAL LETTER ALPHA) or
|
||||
U+0410 (CYRILLIC CAPITAL LETTER A) in the intention of causing a
|
||||
non-match of strings which visually appear the same and/or causing
|
||||
the string to appear elsewhere in a sort.
|
||||
|
||||
5. IANA Considerations
|
||||
|
||||
The i;unicode-casemap collation defined in section 2 has been added
|
||||
to the registry of collations defined in [COMPARATOR].
|
||||
|
||||
6. Normative References
|
||||
|
||||
[COMPARATOR] Newman, C., Duerst, M., and A. Gulbrandsen,
|
||||
"Internet Application Protocol Collation
|
||||
Registry", RFC 4790, February 2007.
|
||||
|
||||
[STRINGPREP] Hoffman, P. and M. Blanchet, "Preparation of
|
||||
Internationalized Strings ("stringprep")", RFC
|
||||
3454, December 2002.
|
||||
|
||||
[UTF-8] Yergeau, F., "UTF-8, a transformation format of
|
||||
ISO 10646", STD 63, RFC 3629, November 2003.
|
||||
|
||||
[UNICODE-DATA] <http://www.unicode.org/Public/UNIDATA/
|
||||
UnicodeData.txt>
|
||||
|
||||
Although the UnicodeData.txt file referenced
|
||||
here is part of the Unicode standard, it is
|
||||
subject to change as new characters are added
|
||||
to Unicode and errors are corrected in Unicode
|
||||
revisions. As a result, it may be less stable
|
||||
than might otherwise be implied by the
|
||||
standards status of this specification.
|
||||
|
||||
[UNICODE-SECURITY] Davis, M. and M. Suignard, "Unicode Security
|
||||
Considerations", February 2006,
|
||||
<http://www.unicode.org/reports/tr36/>.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crispin Standards Track [Page 5]
|
||||
|
||||
RFC 5051 i;unicode-casemap October 2007
|
||||
|
||||
|
||||
7. Informative References
|
||||
|
||||
[BASIC] Newman, C., Duerst, M., and A. Gulbrandsen,
|
||||
"i;basic - the Unicode Collation Algorithm",
|
||||
Work in Progress, March 2007.
|
||||
|
||||
[IMAP-SORT] Crispin, M. and K. Murchison, "Internet Message
|
||||
Access Protocol - SORT and THREAD Extensions",
|
||||
Work in Progress, September 2007.
|
||||
|
||||
Author's Address
|
||||
|
||||
Mark R. Crispin
|
||||
Networks and Distributed Computing
|
||||
University of Washington
|
||||
4545 15th Avenue NE
|
||||
Seattle, WA 98105-4527
|
||||
|
||||
Phone: +1 (206) 543-5762
|
||||
EMail: MRC@CAC.Washington.EDU
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crispin Standards Track [Page 6]
|
||||
|
||||
RFC 5051 i;unicode-casemap October 2007
|
||||
|
||||
|
||||
Full Copyright Statement
|
||||
|
||||
Copyright (C) The IETF Trust (2007).
|
||||
|
||||
This document is subject to the rights, licenses and restrictions
|
||||
contained in BCP 78, and except as set forth therein, the authors
|
||||
retain all their rights.
|
||||
|
||||
This document and the information contained herein are provided on an
|
||||
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
|
||||
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
|
||||
THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
|
||||
THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
Intellectual Property
|
||||
|
||||
The IETF takes no position regarding the validity or scope of any
|
||||
Intellectual Property Rights or other rights that might be claimed to
|
||||
pertain to the implementation or use of the technology described in
|
||||
this document or the extent to which any license under such rights
|
||||
might or might not be available; nor does it represent that it has
|
||||
made any independent effort to identify any such rights. Information
|
||||
on the procedures with respect to rights in RFC documents can be
|
||||
found in BCP 78 and BCP 79.
|
||||
|
||||
Copies of IPR disclosures made to the IETF Secretariat and any
|
||||
assurances of licenses to be made available, or the result of an
|
||||
attempt made to obtain a general license or permission for the use of
|
||||
such proprietary rights by implementers or users of this
|
||||
specification can be obtained from the IETF on-line IPR repository at
|
||||
http://www.ietf.org/ipr.
|
||||
|
||||
The IETF invites any interested party to bring to its attention any
|
||||
copyrights, patents or patent applications, or other proprietary
|
||||
rights that may cover technology that may be required to implement
|
||||
this standard. Please address the information to the IETF at
|
||||
ietf-ipr@ietf.org.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Crispin Standards Track [Page 7]
|
||||
|
281
dav/SabreDAV/docs/rfc5397.txt
Normal file
281
dav/SabreDAV/docs/rfc5397.txt
Normal file
|
@ -0,0 +1,281 @@
|
|||
|
||||
|
||||
|
||||
Network Working Group W. Sanchez
|
||||
Request for Comments: 5397 C. Daboo
|
||||
Category: Standards Track Apple Inc.
|
||||
December 2008
|
||||
|
||||
|
||||
WebDAV Current Principal Extension
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This document specifies an Internet standards track protocol for the
|
||||
Internet community, and requests discussion and suggestions for
|
||||
improvements. Please refer to the current edition of the "Internet
|
||||
Official Protocol Standards" (STD 1) for the standardization state
|
||||
and status of this protocol. Distribution of this memo is unlimited.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2008 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document.
|
||||
|
||||
Abstract
|
||||
|
||||
This specification defines a new WebDAV property that allows clients
|
||||
to quickly determine the principal corresponding to the current
|
||||
authenticated user.
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
|
||||
2. Conventions Used in This Document . . . . . . . . . . . . . . . 2
|
||||
3. DAV:current-user-principal . . . . . . . . . . . . . . . . . . 3
|
||||
4. Security Considerations . . . . . . . . . . . . . . . . . . . . 4
|
||||
5. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . 4
|
||||
6. Normative References . . . . . . . . . . . . . . . . . . . . . 4
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Sanchez & Daboo Standards Track [Page 1]
|
||||
|
||||
RFC 5397 WebDAV Current Principal December 2008
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
WebDAV [RFC4918] is an extension to HTTP [RFC2616] to support
|
||||
improved document authoring capabilities. The WebDAV Access Control
|
||||
Protocol ("WebDAV ACL") [RFC3744] extension adds access control
|
||||
capabilities to WebDAV. It introduces the concept of a "principal"
|
||||
resource, which is used to represent information about authenticated
|
||||
entities on the system.
|
||||
|
||||
Some clients have a need to determine which [RFC3744] principal a
|
||||
server is associating with the currently authenticated HTTP user.
|
||||
While [RFC3744] defines a DAV:current-user-privilege-set property for
|
||||
retrieving the privileges granted to that principal, there is no
|
||||
recommended way to identify the principal in question, which is
|
||||
necessary to perform other useful operations. For example, a client
|
||||
may wish to determine which groups the current user is a member of,
|
||||
or modify a property of the principal resource associated with the
|
||||
current user.
|
||||
|
||||
The DAV:principal-match REPORT provides some useful functionality,
|
||||
but there are common situations where the results from that query can
|
||||
be ambiguous. For example, not only is an individual user principal
|
||||
returned, but also every group principal that the user is a member
|
||||
of, and there is no clear way to distinguish which is which.
|
||||
|
||||
This specification proposes an extension to WebDAV ACL that adds a
|
||||
DAV:current-user-principal property to resources under access control
|
||||
on the server. This property provides a URL to a principal resource
|
||||
corresponding to the currently authenticated user. This allows a
|
||||
client to "bootstrap" itself by performing additional queries on the
|
||||
principal resource to obtain additional information from that
|
||||
resource, which is the purpose of this extension. Note that while it
|
||||
is possible for multiple URLs to refer to the same principal
|
||||
resource, or for multiple principal resources to correspond to a
|
||||
single principal, this specification only allows for a single http(s)
|
||||
URL in the DAV:current-user-principal property. If a client wishes
|
||||
to obtain alternate URLs for the principal, it can query the
|
||||
principal resource for this information; it is not the purpose of
|
||||
this extension to provide a complete list of such URLs, but simply to
|
||||
provide a means to locate a resource which contains that (and other)
|
||||
information.
|
||||
|
||||
2. Conventions Used in This Document
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
|
||||
|
||||
|
||||
Sanchez & Daboo Standards Track [Page 2]
|
||||
|
||||
RFC 5397 WebDAV Current Principal December 2008
|
||||
|
||||
|
||||
When XML element types in the namespace "DAV:" are referenced in this
|
||||
document outside of the context of an XML fragment, the string "DAV:"
|
||||
will be prefixed to the element type names.
|
||||
|
||||
Processing of XML by clients and servers MUST follow the rules
|
||||
defined in Section 17 of WebDAV [RFC4918].
|
||||
|
||||
Some of the declarations refer to XML elements defined by WebDAV
|
||||
[RFC4918].
|
||||
|
||||
3. DAV:current-user-principal
|
||||
|
||||
Name: current-user-principal
|
||||
|
||||
Namespace: DAV:
|
||||
|
||||
Purpose: Indicates a URL for the currently authenticated user's
|
||||
principal resource on the server.
|
||||
|
||||
Value: A single DAV:href or DAV:unauthenticated element.
|
||||
|
||||
Protected: This property is computed on a per-request basis, and
|
||||
therefore is protected.
|
||||
|
||||
Description: The DAV:current-user-principal property contains either
|
||||
a DAV:href or DAV:unauthenticated XML element. The DAV:href
|
||||
element contains a URL to a principal resource corresponding to
|
||||
the currently authenticated user. That URL MUST be one of the
|
||||
URLs in the DAV:principal-URL or DAV:alternate-URI-set properties
|
||||
defined on the principal resource and MUST be an http(s) scheme
|
||||
URL. When authentication has not been done or has failed, this
|
||||
property MUST contain the DAV:unauthenticated pseudo-principal.
|
||||
|
||||
In some cases, there may be multiple principal resources
|
||||
corresponding to the same authenticated principal. In that case,
|
||||
the server is free to choose any one of the principal resource
|
||||
URIs for the value of the DAV:current-user-principal property.
|
||||
However, servers SHOULD be consistent and use the same principal
|
||||
resource URI for each authenticated principal.
|
||||
|
||||
COPY/MOVE behavior: This property is computed on a per-request
|
||||
basis, and is thus never copied or moved.
|
||||
|
||||
Definition:
|
||||
|
||||
<!ELEMENT current-user-principal (unauthenticated | href)>
|
||||
<!-- href value: a URL to a principal resource -->
|
||||
|
||||
|
||||
|
||||
|
||||
Sanchez & Daboo Standards Track [Page 3]
|
||||
|
||||
RFC 5397 WebDAV Current Principal December 2008
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
<D:current-user-principal xmlns:D="DAV:">
|
||||
<D:href>/principals/users/cdaboo</D:href>
|
||||
</D:current-user-principal>
|
||||
|
||||
4. Security Considerations
|
||||
|
||||
This specification does not introduce any additional security issues
|
||||
beyond those defined for HTTP [RFC2616], WebDAV [RFC4918], and WebDAV
|
||||
ACL [RFC3744].
|
||||
|
||||
5. Acknowledgments
|
||||
|
||||
This specification is based on discussions that took place within the
|
||||
Calendaring and Scheduling Consortium's CalDAV Technical Committee.
|
||||
The authors thank the participants of that group for their input.
|
||||
|
||||
The authors thank Julian Reschke for his valuable input via the
|
||||
WebDAV working group mailing list.
|
||||
|
||||
6. Normative References
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
|
||||
Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
|
||||
Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
|
||||
|
||||
[RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web
|
||||
Distributed Authoring and Versioning (WebDAV)
|
||||
Access Control Protocol", RFC 3744, May 2004.
|
||||
|
||||
[RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed
|
||||
Authoring and Versioning (WebDAV)", RFC 4918, June 2007.
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Wilfredo Sanchez
|
||||
Apple Inc.
|
||||
1 Infinite Loop
|
||||
Cupertino, CA 95014
|
||||
USA
|
||||
|
||||
EMail: wsanchez@wsanchez.net
|
||||
URI: http://www.apple.com/
|
||||
|
||||
|
||||
|
||||
|
||||
Sanchez & Daboo Standards Track [Page 4]
|
||||
|
||||
RFC 5397 WebDAV Current Principal December 2008
|
||||
|
||||
|
||||
Cyrus Daboo
|
||||
Apple Inc.
|
||||
1 Infinite Loop
|
||||
Cupertino, CA 95014
|
||||
USA
|
||||
|
||||
EMail: cyrus@daboo.name
|
||||
URI: http://www.apple.com/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Sanchez & Daboo Standards Track [Page 5]
|
||||
|
||||
|
9411
dav/SabreDAV/docs/rfc5545.txt
Normal file
9411
dav/SabreDAV/docs/rfc5545.txt
Normal file
File diff suppressed because it is too large
Load diff
7451
dav/SabreDAV/docs/rfc5546.txt
Normal file
7451
dav/SabreDAV/docs/rfc5546.txt
Normal file
File diff suppressed because it is too large
Load diff
675
dav/SabreDAV/docs/rfc5689.txt
Normal file
675
dav/SabreDAV/docs/rfc5689.txt
Normal file
|
@ -0,0 +1,675 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group C. Daboo
|
||||
Request for Comments: 5689 Apple Inc.
|
||||
Updates: 4791, 4918 September 2009
|
||||
Category: Standards Track
|
||||
|
||||
|
||||
Extended MKCOL for Web Distributed Authoring and Versioning (WebDAV)
|
||||
|
||||
Abstract
|
||||
|
||||
This specification extends the Web Distributed Authoring and
|
||||
Versioning (WebDAV) MKCOL (Make Collection) method to allow
|
||||
collections of arbitrary resourcetype to be created and to allow
|
||||
properties to be set at the same time.
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This document specifies an Internet standards track protocol for the
|
||||
Internet community, and requests discussion and suggestions for
|
||||
improvements. Please refer to the current edition of the "Internet
|
||||
Official Protocol Standards" (STD 1) for the standardization state
|
||||
and status of this protocol. Distribution of this memo is unlimited.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2009 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the BSD License.
|
||||
|
||||
This document may contain material from IETF Documents or IETF
|
||||
Contributions published or made publicly available before November
|
||||
10, 2008. The person(s) controlling the copyright in some of this
|
||||
material may not have granted the IETF Trust the right to allow
|
||||
modifications of such material outside the IETF Standards Process.
|
||||
Without obtaining an adequate license from the person(s) controlling
|
||||
the copyright in such materials, this document may not be modified
|
||||
outside the IETF Standards Process, and derivative works of it may
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 1]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
not be created outside the IETF Standards Process, except to format
|
||||
it for publication as an RFC or to translate it into languages other
|
||||
than English.
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
2. Conventions Used in This Document . . . . . . . . . . . . . . 3
|
||||
3. WebDAV Extended MKCOL . . . . . . . . . . . . . . . . . . . . 4
|
||||
3.1. Extended MKCOL Support . . . . . . . . . . . . . . . . . . 5
|
||||
3.1.1. Example: Using OPTIONS for the Discovery of
|
||||
Support for Extended MKCOL . . . . . . . . . . . . . . 5
|
||||
3.2. Status Codes . . . . . . . . . . . . . . . . . . . . . . . 5
|
||||
3.3. Additional Precondition for Extended MKCOL . . . . . . . . 5
|
||||
3.4. Example: Successful Extended MKCOL Request . . . . . . . . 6
|
||||
3.5. Example: Unsuccessful Extended MKCOL Request . . . . . . . 6
|
||||
4. Using Extended MKCOL as an Alternative for MKxxx Methods . . . 8
|
||||
4.1. MKCALENDAR Alternative . . . . . . . . . . . . . . . . . . 8
|
||||
4.1.1. Example: Using MKCOL Instead of MKCALENDAR . . . . . . 8
|
||||
5. XML Element Definitions . . . . . . . . . . . . . . . . . . . 10
|
||||
5.1. mkcol XML Element . . . . . . . . . . . . . . . . . . . . 10
|
||||
5.2. mkcol-response XML Element . . . . . . . . . . . . . . . . 10
|
||||
6. Security Considerations . . . . . . . . . . . . . . . . . . . 11
|
||||
7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 11
|
||||
8. Normative References . . . . . . . . . . . . . . . . . . . . . 11
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 2]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
WebDAV [RFC4918] defines the HTTP [RFC2616] method MKCOL. This
|
||||
method is used to create WebDAV collections on the server. However,
|
||||
several WebDAV-based specifications (e.g., CalDAV [RFC4791]) define
|
||||
"special" collections -- ones that are identified by additional
|
||||
values in the DAV:resourcetype property assigned to the collection
|
||||
resource or by other means. These "special" collections are created
|
||||
by new methods (e.g., MKCALENDAR). The addition of a new MKxxx
|
||||
method for each new "special" collection adds to server complexity
|
||||
and is detrimental to overall reliability due to the need to make
|
||||
sure intermediaries are aware of these methods.
|
||||
|
||||
This specification defines an extension to the WebDAV MKCOL method
|
||||
that adds a request body allowing a client to specify WebDAV
|
||||
properties to be set on the newly created collection or resource. In
|
||||
particular, the DAV:resourcetype property can be used to create a
|
||||
"special" collection; alternatively, other properties can be used to
|
||||
create a "special" resource. This avoids the need to invent new
|
||||
MKxxx methods.
|
||||
|
||||
2. Conventions Used in This Document
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in [RFC2119].
|
||||
|
||||
This document uses XML DTD fragments (Section 3.2 of
|
||||
[W3C.REC-xml-20081126]) as a purely notational convention. WebDAV
|
||||
request and response bodies cannot be validated by a DTD due to the
|
||||
specific extensibility rules defined in Section 17 of [RFC4918] and
|
||||
due to the fact that all XML elements defined by this specification
|
||||
use the XML namespace name "DAV:". In particular:
|
||||
|
||||
1. Element names use the "DAV:" namespace.
|
||||
|
||||
2. Element ordering is irrelevant unless explicitly stated.
|
||||
|
||||
3. Extension elements (elements not already defined as valid child
|
||||
elements) may be added anywhere, except when explicitly stated
|
||||
otherwise.
|
||||
|
||||
4. Extension attributes (attributes not already defined as valid for
|
||||
this element) may be added anywhere, except when explicitly
|
||||
stated otherwise.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 3]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
When an XML element type in the "DAV:" namespace is referenced in
|
||||
this document outside of the context of an XML fragment, the string
|
||||
"DAV:" will be prefixed to the element type.
|
||||
|
||||
This document inherits, and sometimes extends, DTD productions from
|
||||
Section 14 of [RFC4918].
|
||||
|
||||
3. WebDAV Extended MKCOL
|
||||
|
||||
The WebDAV MKCOL request is extended to allow the inclusion of a
|
||||
request body. The request body is an XML document containing a
|
||||
single DAV:mkcol XML element as the root element. The Content-Type
|
||||
request header MUST be set appropriately for an XML body (e.g., set
|
||||
to "text/xml" or "application/xml"). XML-typed bodies for an MKCOL
|
||||
request that do not have DAV:mkcol as the root element are reserved
|
||||
for future usage.
|
||||
|
||||
One or more DAV:set XML elements may be included in the DAV:mkcol XML
|
||||
element to allow setting properties on the collection as it is
|
||||
created. In particular, to create a collection of a particular type,
|
||||
the DAV:resourcetype XML element MUST be included in a DAV:set XML
|
||||
element and MUST specify the expected resource type elements for the
|
||||
new resource, which MUST include the DAV:collection element that
|
||||
needs to be present for any WebDAV collection.
|
||||
|
||||
As per the PROPPATCH method (Section 9.2 of [RFC4918]), servers MUST
|
||||
process any DAV:set instructions in document order (an exception to
|
||||
the normal rule that ordering is irrelevant). If any one instruction
|
||||
fails to execute successfully, all instructions MUST fail (i.e.,
|
||||
either all succeed or all fail). Thus, if any error occurs during
|
||||
processing, all executed instructions MUST be undone and a proper
|
||||
error result returned. Failure to set a property value on the
|
||||
collection MUST result in a failure of the overall MKCOL request --
|
||||
i.e., the collection is not created.
|
||||
|
||||
The response to an extended MKCOL request MUST be an XML document
|
||||
containing a single DAV:mkcol-response XML element, which MUST
|
||||
contain DAV:propstat XML elements with the status of each property
|
||||
when the request fails due to a failure to set one or more of the
|
||||
properties specified in the request body. The server MAY return a
|
||||
response body in the case where the request is successful, indicating
|
||||
success for setting each property specified in the request body.
|
||||
When an empty response body is returned with a success request status
|
||||
code, the client can assume that all properties were set.
|
||||
|
||||
In all other respects, the behavior of the extended MKCOL request
|
||||
follows that of the standard MKCOL request.
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 4]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
3.1. Extended MKCOL Support
|
||||
|
||||
A server supporting the features described in this document MUST
|
||||
include "extended-mkcol" as a field in the DAV response header from
|
||||
an OPTIONS request on any URI that supports use of the extended MKCOL
|
||||
method.
|
||||
|
||||
3.1.1. Example: Using OPTIONS for the Discovery of Support for Extended
|
||||
MKCOL
|
||||
|
||||
>> Request <<
|
||||
|
||||
OPTIONS /addressbooks/users/ HTTP/1.1
|
||||
Host: addressbook.example.com
|
||||
|
||||
>> Response <<
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE
|
||||
Allow: MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL
|
||||
DAV: 1, 2, 3, access-control, extended-mkcol
|
||||
Date: Sat, 11 Nov 2006 09:32:12 GMT
|
||||
Content-Length: 0
|
||||
|
||||
3.2. Status Codes
|
||||
|
||||
As per Section 9.3.1 of [RFC4918].
|
||||
|
||||
3.3. Additional Precondition for Extended MKCOL
|
||||
|
||||
WebDAV ([RFC4918], Section 16) defines preconditions and
|
||||
postconditions for request behavior. This specification adds the
|
||||
following precondition for the extended MKCOL request.
|
||||
|
||||
Name: valid-resourcetype
|
||||
|
||||
Namespace: DAV:
|
||||
|
||||
Use with: Typically 403 (Forbidden)
|
||||
|
||||
Purpose: (precondition) -- The server MUST support the specified
|
||||
resourcetype value for the specified collection.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 5]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
3.4. Example: Successful Extended MKCOL Request
|
||||
|
||||
This example shows how the extended MKCOL request is used to create a
|
||||
collection of a fictitious type "special-resource". The response
|
||||
body is empty as the request completed successfully.
|
||||
|
||||
>> Request <<
|
||||
|
||||
MKCOL /home/special/ HTTP/1.1
|
||||
Host: special.example.com
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:mkcol xmlns:D="DAV:"
|
||||
xmlns:E="http://example.com/ns/">
|
||||
<D:set>
|
||||
<D:prop>
|
||||
<D:resourcetype>
|
||||
<D:collection/>
|
||||
<E:special-resource/>
|
||||
</D:resourcetype>
|
||||
<D:displayname>Special Resource</D:displayname>
|
||||
</D:prop>
|
||||
</D:set>
|
||||
</D:mkcol>
|
||||
|
||||
>> Response <<
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Cache-Control: no-cache
|
||||
Date: Sat, 11 Nov 2006 09:32:12 GMT
|
||||
|
||||
3.5. Example: Unsuccessful Extended MKCOL Request
|
||||
|
||||
This example shows an attempt to use the extended MKCOL request to
|
||||
create a collection of a fictitious type "special-resource", which is
|
||||
not actually supported by the server. The response body shows that
|
||||
an error occurred specifically with the DAV:resourcetype property.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 6]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
>> Request <<
|
||||
|
||||
MKCOL /home/special/ HTTP/1.1
|
||||
Host: special.example.com
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:mkcol xmlns:D="DAV:"
|
||||
xmlns:E="http://example.com/ns/">
|
||||
<D:set>
|
||||
<D:prop>
|
||||
<D:resourcetype>
|
||||
<D:collection/>
|
||||
<E:special-resource/>
|
||||
</D:resourcetype>
|
||||
<D:displayname>Special Resource</D:displayname>
|
||||
</D:prop>
|
||||
</D:set>
|
||||
</D:mkcol>
|
||||
|
||||
>> Response <<
|
||||
|
||||
HTTP/1.1 403 Forbidden
|
||||
Cache-Control: no-cache
|
||||
Date: Sat, 11 Nov 2006 09:32:12 GMT
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:mkcol-response xmlns:D="DAV:">
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:resourcetype/>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 403 Forbidden</D:status>
|
||||
<D:error><D:valid-resourcetype /></D:error>
|
||||
<D:responsedescription>Resource type is not
|
||||
supported by this server</D:responsedescription>
|
||||
</D:propstat>
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:displayname/>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 424 Failed Dependency</D:status>
|
||||
</D:propstat>
|
||||
</D:mkcol-response>
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 7]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
4. Using Extended MKCOL as an Alternative for MKxxx Methods
|
||||
|
||||
One of the goals of this extension is to eliminate the need for other
|
||||
extensions to define their own variant of MKCOL to create the special
|
||||
collections they need. This extension can be used as an alternative
|
||||
to existing MKxxx methods in other extensions as detailed below. If
|
||||
a server supports this extension and the other extension listed, then
|
||||
the server MUST support use of the extended MKCOL method to achieve
|
||||
the same result as the MKxxx method of the other extension.
|
||||
|
||||
4.1. MKCALENDAR Alternative
|
||||
|
||||
CalDAV defines the MKCALENDAR method to create a calendar collection
|
||||
as well as to set properties during creation (Section 5.3.1 of
|
||||
[RFC4791]).
|
||||
|
||||
The extended MKCOL method can be used instead by specifying both DAV:
|
||||
collection and CALDAV:calendar-collection XML elements in the DAV:
|
||||
resourcetype property, set during the extended MKCOL request.
|
||||
|
||||
4.1.1. Example: Using MKCOL Instead of MKCALENDAR
|
||||
|
||||
The first example below shows an MKCALENDAR request containing a
|
||||
CALDAV:mkcalendar XML element in the request body and returning a
|
||||
CALDAV:mkcalendar-response XML element in the response body.
|
||||
|
||||
>> MKCALENDAR Request <<
|
||||
|
||||
MKCALENDAR /home/lisa/calendars/events/ HTTP/1.1
|
||||
Host: calendar.example.com
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<C:mkcalendar xmlns:D="DAV:"
|
||||
xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:set>
|
||||
<D:prop>
|
||||
<D:displayname>Lisa's Events</D:displayname>
|
||||
</D:prop>
|
||||
</D:set>
|
||||
</C:mkcalendar>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 8]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
>> MKCALENDAR Response <<
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Cache-Control: no-cache
|
||||
Date: Sat, 11 Nov 2006 09:32:12 GMT
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<C:mkcalendar-response xmlns:D="DAV:"
|
||||
xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:displayname/>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</C:mkcalendar-response>
|
||||
|
||||
The second example shows the equivalent extended MKCOL request with
|
||||
the same request and response XML elements.
|
||||
|
||||
>> MKCOL Request <<
|
||||
|
||||
MKCOL /home/lisa/calendars/events/ HTTP/1.1
|
||||
Host: calendar.example.com
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:mkcol xmlns:D="DAV:"
|
||||
xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:set>
|
||||
<D:prop>
|
||||
<D:resourcetype>
|
||||
<D:collection/>
|
||||
<C:calendar/>
|
||||
</D:resourcetype>
|
||||
<D:displayname>Lisa's Events</D:displayname>
|
||||
</D:prop>
|
||||
</D:set>
|
||||
</D:mkcol>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 9]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
>> MKCOL Response <<
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Cache-Control: no-cache
|
||||
Date: Sat, 11 Nov 2006 09:32:12 GMT
|
||||
Content-Type: application/xml; charset="utf-8"
|
||||
Content-Length: xxxx
|
||||
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<D:mkcol-response xmlns:D="DAV:"
|
||||
xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
<D:propstat>
|
||||
<D:prop>
|
||||
<D:resourcetype/>
|
||||
<D:displayname/>
|
||||
</D:prop>
|
||||
<D:status>HTTP/1.1 200 OK</D:status>
|
||||
</D:propstat>
|
||||
</D:mkcol-response>
|
||||
|
||||
5. XML Element Definitions
|
||||
|
||||
5.1. mkcol XML Element
|
||||
|
||||
Name: mkcol
|
||||
|
||||
Namespace: DAV:
|
||||
|
||||
Purpose: Used in a request to specify properties to be set in an
|
||||
extended MKCOL request, as well as any additional information
|
||||
needed when creating the resource.
|
||||
|
||||
Description: This XML element is a container for the information
|
||||
required to modify the properties on a collection resource as it
|
||||
is created in an extended MKCOL request.
|
||||
|
||||
Definition:
|
||||
|
||||
<!ELEMENT mkcol (set+)>
|
||||
|
||||
5.2. mkcol-response XML Element
|
||||
|
||||
Name: mkcol-response
|
||||
|
||||
Namespace: DAV:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 10]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
Purpose: Used in a response to indicate the status of properties
|
||||
that were set or failed to be set during an extended MKCOL
|
||||
request.
|
||||
|
||||
Description: This XML element is a container for the information
|
||||
returned about a resource that has been created in an extended
|
||||
MKCOL request.
|
||||
|
||||
Definition:
|
||||
|
||||
<!ELEMENT mkcol-response (propstat+)>
|
||||
|
||||
6. Security Considerations
|
||||
|
||||
This extension does not introduce any new security concerns beyond
|
||||
those already described in HTTP [RFC2616] and WebDAV [RFC4918].
|
||||
|
||||
7. Acknowledgments
|
||||
|
||||
Thanks to Bernard Desruisseaux, Mike Douglass, Alexey Melnikov,
|
||||
Julian Reschke, and Simon Vaillancourt.
|
||||
|
||||
|
||||
8. Normative References
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
|
||||
Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
|
||||
Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
|
||||
|
||||
[RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault,
|
||||
"Calendaring Extensions to WebDAV (CalDAV)", RFC 4791,
|
||||
March 2007.
|
||||
|
||||
[RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed
|
||||
Authoring and Versioning (WebDAV)", RFC 4918, June 2007.
|
||||
|
||||
[W3C.REC-xml-20081126]
|
||||
Maler, E., Yergeau, F., Paoli, J., Bray, T., and C.
|
||||
Sperberg-McQueen, "Extensible Markup Language (XML) 1.0
|
||||
(Fifth Edition)", World Wide Web Consortium
|
||||
Recommendation REC-xml-20081126, November 2008,
|
||||
<http://www.w3.org/TR/2008/REC-xml-20081126>.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 11]
|
||||
|
||||
RFC 5689 Extended MKCOL for WebDAV September 2009
|
||||
|
||||
|
||||
Author's Address
|
||||
|
||||
Cyrus Daboo
|
||||
Apple Inc.
|
||||
1 Infinite Loop
|
||||
Cupertino, CA 95014
|
||||
USA
|
||||
|
||||
EMail: cyrus@daboo.name
|
||||
URI: http://www.apple.com/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Daboo Standards Track [Page 12]
|
||||
|
563
dav/SabreDAV/docs/rfc5789.txt
Normal file
563
dav/SabreDAV/docs/rfc5789.txt
Normal file
|
@ -0,0 +1,563 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Internet Engineering Task Force (IETF) L. Dusseault
|
||||
Request for Comments: 5789 Linden Lab
|
||||
Category: Standards Track J. Snell
|
||||
ISSN: 2070-1721 March 2010
|
||||
|
||||
|
||||
PATCH Method for HTTP
|
||||
|
||||
Abstract
|
||||
|
||||
Several applications extending the Hypertext Transfer Protocol (HTTP)
|
||||
require a feature to do partial resource modification. The existing
|
||||
HTTP PUT method only allows a complete replacement of a document.
|
||||
This proposal adds a new HTTP method, PATCH, to modify an existing
|
||||
HTTP resource.
|
||||
|
||||
Status of This Memo
|
||||
|
||||
This is an Internet Standards Track document.
|
||||
|
||||
This document is a product of the Internet Engineering Task Force
|
||||
(IETF). It represents the consensus of the IETF community. It has
|
||||
received public review and has been approved for publication by the
|
||||
Internet Engineering Steering Group (IESG). Further information on
|
||||
Internet Standards is available in Section 2 of RFC 5741.
|
||||
|
||||
Information about the current status of this document, any errata,
|
||||
and how to provide feedback on it may be obtained at
|
||||
http://www.rfc-editor.org/info/rfc5789.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2010 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 1]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction ....................................................2
|
||||
2. The PATCH Method ................................................2
|
||||
2.1. A Simple PATCH Example .....................................4
|
||||
2.2. Error Handling .............................................5
|
||||
3. Advertising Support in OPTIONS ..................................7
|
||||
3.1. The Accept-Patch Header ....................................7
|
||||
3.2. Example OPTIONS Request and Response .......................7
|
||||
4. IANA Considerations .............................................8
|
||||
4.1. The Accept-Patch Response Header ...........................8
|
||||
5. Security Considerations .........................................8
|
||||
6. References ......................................................9
|
||||
6.1. Normative References .......................................9
|
||||
6.2. Informative References .....................................9
|
||||
Appendix A. Acknowledgements .....................................10
|
||||
|
||||
1. Introduction
|
||||
|
||||
This specification defines the new HTTP/1.1 [RFC2616] method, PATCH,
|
||||
which is used to apply partial modifications to a resource.
|
||||
|
||||
A new method is necessary to improve interoperability and prevent
|
||||
errors. The PUT method is already defined to overwrite a resource
|
||||
with a complete new body, and cannot be reused to do partial changes.
|
||||
Otherwise, proxies and caches, and even clients and servers, may get
|
||||
confused as to the result of the operation. POST is already used but
|
||||
without broad interoperability (for one, there is no standard way to
|
||||
discover patch format support). PATCH was mentioned in earlier HTTP
|
||||
specifications, but not completely defined.
|
||||
|
||||
In this document, the key words "MUST", "MUST NOT", "REQUIRED",
|
||||
"SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
|
||||
and "OPTIONAL" are to be interpreted as described in [RFC2119].
|
||||
|
||||
Furthermore, this document uses the ABNF syntax defined in Section
|
||||
2.1 of [RFC2616].
|
||||
|
||||
2. The PATCH Method
|
||||
|
||||
The PATCH method requests that a set of changes described in the
|
||||
request entity be applied to the resource identified by the Request-
|
||||
URI. The set of changes is represented in a format called a "patch
|
||||
document" identified by a media type. If the Request-URI does not
|
||||
point to an existing resource, the server MAY create a new resource,
|
||||
depending on the patch document type (whether it can logically modify
|
||||
a null resource) and permissions, etc.
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 2]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
The difference between the PUT and PATCH requests is reflected in the
|
||||
way the server processes the enclosed entity to modify the resource
|
||||
identified by the Request-URI. In a PUT request, the enclosed entity
|
||||
is considered to be a modified version of the resource stored on the
|
||||
origin server, and the client is requesting that the stored version
|
||||
be replaced. With PATCH, however, the enclosed entity contains a set
|
||||
of instructions describing how a resource currently residing on the
|
||||
origin server should be modified to produce a new version. The PATCH
|
||||
method affects the resource identified by the Request-URI, and it
|
||||
also MAY have side effects on other resources; i.e., new resources
|
||||
may be created, or existing ones modified, by the application of a
|
||||
PATCH.
|
||||
|
||||
PATCH is neither safe nor idempotent as defined by [RFC2616], Section
|
||||
9.1.
|
||||
|
||||
A PATCH request can be issued in such a way as to be idempotent,
|
||||
which also helps prevent bad outcomes from collisions between two
|
||||
PATCH requests on the same resource in a similar time frame.
|
||||
Collisions from multiple PATCH requests may be more dangerous than
|
||||
PUT collisions because some patch formats need to operate from a
|
||||
known base-point or else they will corrupt the resource. Clients
|
||||
using this kind of patch application SHOULD use a conditional request
|
||||
such that the request will fail if the resource has been updated
|
||||
since the client last accessed the resource. For example, the client
|
||||
can use a strong ETag [RFC2616] in an If-Match header on the PATCH
|
||||
request.
|
||||
|
||||
There are also cases where patch formats do not need to operate from
|
||||
a known base-point (e.g., appending text lines to log files, or non-
|
||||
colliding rows to database tables), in which case the same care in
|
||||
client requests is not needed.
|
||||
|
||||
The server MUST apply the entire set of changes atomically and never
|
||||
provide (e.g., in response to a GET during this operation) a
|
||||
partially modified representation. If the entire patch document
|
||||
cannot be successfully applied, then the server MUST NOT apply any of
|
||||
the changes. The determination of what constitutes a successful
|
||||
PATCH can vary depending on the patch document and the type of
|
||||
resource(s) being modified. For example, the common 'diff' utility
|
||||
can generate a patch document that applies to multiple files in a
|
||||
directory hierarchy. The atomicity requirement holds for all
|
||||
directly affected files. See "Error Handling", Section 2.2, for
|
||||
details on status codes and possible error conditions.
|
||||
|
||||
If the request passes through a cache and the Request-URI identifies
|
||||
one or more currently cached entities, those entries SHOULD be
|
||||
treated as stale. A response to this method is only cacheable if it
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 3]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
contains explicit freshness information (such as an Expires header or
|
||||
"Cache-Control: max-age" directive) as well as the Content-Location
|
||||
header matching the Request-URI, indicating that the PATCH response
|
||||
body is a resource representation. A cached PATCH response can only
|
||||
be used to respond to subsequent GET and HEAD requests; it MUST NOT
|
||||
be used to respond to other methods (in particular, PATCH).
|
||||
|
||||
Note that entity-headers contained in the request apply only to the
|
||||
contained patch document and MUST NOT be applied to the resource
|
||||
being modified. Thus, a Content-Language header could be present on
|
||||
the request, but it would only mean (for whatever that's worth) that
|
||||
the patch document had a language. Servers SHOULD NOT store such
|
||||
headers except as trace information, and SHOULD NOT use such header
|
||||
values the same way they might be used on PUT requests. Therefore,
|
||||
this document does not specify a way to modify a document's Content-
|
||||
Type or Content-Language value through headers, though a mechanism
|
||||
could well be designed to achieve this goal through a patch document.
|
||||
|
||||
There is no guarantee that a resource can be modified with PATCH.
|
||||
Further, it is expected that different patch document formats will be
|
||||
appropriate for different types of resources and that no single
|
||||
format will be appropriate for all types of resources. Therefore,
|
||||
there is no single default patch document format that implementations
|
||||
are required to support. Servers MUST ensure that a received patch
|
||||
document is appropriate for the type of resource identified by the
|
||||
Request-URI.
|
||||
|
||||
Clients need to choose when to use PATCH rather than PUT. For
|
||||
example, if the patch document size is larger than the size of the
|
||||
new resource data that would be used in a PUT, then it might make
|
||||
sense to use PUT instead of PATCH. A comparison to POST is even more
|
||||
difficult, because POST is used in widely varying ways and can
|
||||
encompass PUT and PATCH-like operations if the server chooses. If
|
||||
the operation does not modify the resource identified by the Request-
|
||||
URI in a predictable way, POST should be considered instead of PATCH
|
||||
or PUT.
|
||||
|
||||
2.1. A Simple PATCH Example
|
||||
|
||||
PATCH /file.txt HTTP/1.1
|
||||
Host: www.example.com
|
||||
Content-Type: application/example
|
||||
If-Match: "e0023aa4e"
|
||||
Content-Length: 100
|
||||
|
||||
[description of changes]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 4]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
This example illustrates use of a hypothetical patch document on an
|
||||
existing resource.
|
||||
|
||||
Successful PATCH response to existing text file:
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
Content-Location: /file.txt
|
||||
ETag: "e0023aa4f"
|
||||
|
||||
The 204 response code is used because the response does not carry a
|
||||
message body (which a response with the 200 code would have). Note
|
||||
that other success codes could be used as well.
|
||||
|
||||
Furthermore, the ETag response header field contains the ETag for the
|
||||
entity created by applying the PATCH, available at
|
||||
http://www.example.com/file.txt, as indicated by the Content-Location
|
||||
response header field.
|
||||
|
||||
2.2. Error Handling
|
||||
|
||||
There are several known conditions under which a PATCH request can
|
||||
fail.
|
||||
|
||||
Malformed patch document: When the server determines that the patch
|
||||
document provided by the client is not properly formatted, it
|
||||
SHOULD return a 400 (Bad Request) response. The definition of
|
||||
badly formatted depends on the patch document chosen.
|
||||
|
||||
Unsupported patch document: Can be specified using a 415
|
||||
(Unsupported Media Type) response when the client sends a patch
|
||||
document format that the server does not support for the resource
|
||||
identified by the Request-URI. Such a response SHOULD include an
|
||||
Accept-Patch response header as described in Section 3.1 to notify
|
||||
the client what patch document media types are supported.
|
||||
|
||||
Unprocessable request: Can be specified with a 422 (Unprocessable
|
||||
Entity) response ([RFC4918], Section 11.2) when the server
|
||||
understands the patch document and the syntax of the patch
|
||||
document appears to be valid, but the server is incapable of
|
||||
processing the request. This might include attempts to modify a
|
||||
resource in a way that would cause the resource to become invalid;
|
||||
for instance, a modification to a well-formed XML document that
|
||||
would cause it to no longer be well-formed. There may also be
|
||||
more specific errors like "Conflicting State" that could be
|
||||
signaled with this status code, but the more specific error would
|
||||
generally be more helpful.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 5]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
Resource not found: Can be specified with a 404 (Not Found) status
|
||||
code when the client attempted to apply a patch document to a non-
|
||||
existent resource, but the patch document chosen cannot be applied
|
||||
to a non-existent resource.
|
||||
|
||||
Conflicting state: Can be specified with a 409 (Conflict) status
|
||||
code when the request cannot be applied given the state of the
|
||||
resource. For example, if the client attempted to apply a
|
||||
structural modification and the structures assumed to exist did
|
||||
not exist (with XML, a patch might specify changing element 'foo'
|
||||
to element 'bar' but element 'foo' might not exist).
|
||||
|
||||
Conflicting modification: When a client uses either the If-Match or
|
||||
If-Unmodified-Since header to define a precondition, and that
|
||||
precondition failed, then the 412 (Precondition Failed) error is
|
||||
most helpful to the client. However, that response makes no sense
|
||||
if there was no precondition on the request. In cases when the
|
||||
server detects a possible conflicting modification and no
|
||||
precondition was defined in the request, the server can return a
|
||||
409 (Conflict) response.
|
||||
|
||||
Concurrent modification: Some applications of PATCH might require
|
||||
the server to process requests in the order in which they are
|
||||
received. If a server is operating under those restrictions, and
|
||||
it receives concurrent requests to modify the same resource, but
|
||||
is unable to queue those requests, the server can usefully
|
||||
indicate this error by using a 409 (Conflict) response.
|
||||
|
||||
Note that the 409 Conflict response gives reasonably consistent
|
||||
information to clients. Depending on the application and the nature
|
||||
of the patch format, the client might be able to reissue the request
|
||||
as is (e.g., an instruction to append a line to a log file), have to
|
||||
retrieve the resource content to recalculate a patch, or have to fail
|
||||
the operation.
|
||||
|
||||
Other HTTP status codes can also be used under the appropriate
|
||||
circumstances.
|
||||
|
||||
The entity body of error responses SHOULD contain enough information
|
||||
to communicate the nature of the error to the client. The content-
|
||||
type of the response entity can vary across implementations.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 6]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
3. Advertising Support in OPTIONS
|
||||
|
||||
A server can advertise its support for the PATCH method by adding it
|
||||
to the listing of allowed methods in the "Allow" OPTIONS response
|
||||
header defined in HTTP/1.1. The PATCH method MAY appear in the
|
||||
"Allow" header even if the Accept-Patch header is absent, in which
|
||||
case the list of allowed patch documents is not advertised.
|
||||
|
||||
3.1. The Accept-Patch Header
|
||||
|
||||
This specification introduces a new response header Accept-Patch used
|
||||
to specify the patch document formats accepted by the server.
|
||||
Accept-Patch SHOULD appear in the OPTIONS response for any resource
|
||||
that supports the use of the PATCH method. The presence of the
|
||||
Accept-Patch header in response to any method is an implicit
|
||||
indication that PATCH is allowed on the resource identified by the
|
||||
Request-URI. The presence of a specific patch document format in
|
||||
this header indicates that that specific format is allowed on the
|
||||
resource identified by the Request-URI.
|
||||
|
||||
Accept-Patch = "Accept-Patch" ":" 1#media-type
|
||||
|
||||
The Accept-Patch header specifies a comma-separated listing of media-
|
||||
types (with optional parameters) as defined by [RFC2616], Section
|
||||
3.7.
|
||||
|
||||
Example:
|
||||
|
||||
Accept-Patch: text/example;charset=utf-8
|
||||
|
||||
3.2. Example OPTIONS Request and Response
|
||||
|
||||
[request]
|
||||
|
||||
OPTIONS /example/buddies.xml HTTP/1.1
|
||||
Host: www.example.com
|
||||
|
||||
[response]
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Allow: GET, PUT, POST, OPTIONS, HEAD, DELETE, PATCH
|
||||
Accept-Patch: application/example, text/example
|
||||
|
||||
The examples show a server that supports PATCH generally using two
|
||||
hypothetical patch document formats.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 7]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
4. IANA Considerations
|
||||
|
||||
4.1. The Accept-Patch Response Header
|
||||
|
||||
The Accept-Patch response header has been added to the permanent
|
||||
registry (see [RFC3864]).
|
||||
|
||||
Header field name: Accept-Patch
|
||||
|
||||
Applicable Protocol: HTTP
|
||||
|
||||
Author/Change controller: IETF
|
||||
|
||||
Specification document: this specification
|
||||
|
||||
5. Security Considerations
|
||||
|
||||
The security considerations for PATCH are nearly identical to the
|
||||
security considerations for PUT ([RFC2616], Section 9.6). These
|
||||
include authorizing requests (possibly through access control and/or
|
||||
authentication) and ensuring that data is not corrupted through
|
||||
transport errors or through accidental overwrites. Whatever
|
||||
mechanisms are used for PUT can be used for PATCH as well. The
|
||||
following considerations apply especially to PATCH.
|
||||
|
||||
A document that is patched might be more likely to be corrupted than
|
||||
a document that is overridden in entirety, but that concern can be
|
||||
addressed through the use of mechanisms such as conditional requests
|
||||
using ETags and the If-Match request header as described in
|
||||
Section 2. If a PATCH request fails, the client can issue a GET
|
||||
request to the resource to see what state it is in. In some cases,
|
||||
the client might be able to check the contents of the resource to see
|
||||
if the PATCH request can be resent, but in other cases, the attempt
|
||||
will just fail and/or a user will have to verify intent. In the case
|
||||
of a failure of the underlying transport channel, where a PATCH
|
||||
response is not received before the channel fails or some other
|
||||
timeout happens, the client might have to issue a GET request to see
|
||||
whether the request was applied. The client might want to ensure
|
||||
that the GET request bypasses caches using mechanisms described in
|
||||
HTTP specifications (see, for example, Section 13.1.6 of [RFC2616]).
|
||||
|
||||
Sometimes an HTTP intermediary might try to detect viruses being sent
|
||||
via HTTP by checking the body of the PUT/POST request or GET
|
||||
response. The PATCH method complicates such watch-keeping because
|
||||
neither the source document nor the patch document might be a virus,
|
||||
yet the result could be. This security consideration is not
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 8]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
materially different from those already introduced by byte-range
|
||||
downloads, downloading patch documents, uploading zipped (compressed)
|
||||
files, and so on.
|
||||
|
||||
Individual patch documents will have their own specific security
|
||||
considerations that will likely vary depending on the types of
|
||||
resources being patched. The considerations for patched binary
|
||||
resources, for instance, will be different than those for patched XML
|
||||
documents. Servers MUST take adequate precautions to ensure that
|
||||
malicious clients cannot consume excessive server resources (e.g.,
|
||||
CPU, disk I/O) through the client's use of PATCH.
|
||||
|
||||
6. References
|
||||
|
||||
6.1. Normative References
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
|
||||
Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
|
||||
Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
|
||||
|
||||
[RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration
|
||||
Procedures for Message Header Fields", BCP 90, RFC 3864,
|
||||
September 2004.
|
||||
|
||||
6.2. Informative References
|
||||
|
||||
[RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed
|
||||
Authoring and Versioning (WebDAV)", RFC 4918, June 2007.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 9]
|
||||
|
||||
RFC 5789 HTTP PATCH March 2010
|
||||
|
||||
|
||||
Appendix A. Acknowledgements
|
||||
|
||||
PATCH is not a new concept, it first appeared in HTTP in drafts of
|
||||
version 1.1 written by Roy Fielding and Henrik Frystyk and also
|
||||
appears in Section 19.6.1.1 of RFC 2068.
|
||||
|
||||
Thanks to Adam Roach, Chris Sharp, Julian Reschke, Geoff Clemm, Scott
|
||||
Lawrence, Jeffrey Mogul, Roy Fielding, Greg Stein, Jim Luther, Alex
|
||||
Rousskov, Jamie Lokier, Joe Hildebrand, Mark Nottingham, Michael
|
||||
Balloni, Cyrus Daboo, Brian Carpenter, John Klensin, Eliot Lear, SM,
|
||||
and Bernie Hoeneisen for review and advice on this document. In
|
||||
particular, Julian Reschke did repeated reviews, made many useful
|
||||
suggestions, and was critical to the publication of this document.
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Lisa Dusseault
|
||||
Linden Lab
|
||||
945 Battery Street
|
||||
San Francisco, CA 94111
|
||||
USA
|
||||
|
||||
EMail: lisa.dusseault@gmail.com
|
||||
|
||||
|
||||
James M. Snell
|
||||
|
||||
EMail: jasnell@gmail.com
|
||||
URI: http://www.snellspace.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dusseault & Snell Standards Track [Page 10]
|
||||
|
1235
dav/SabreDAV/docs/rfc6047.txt
Normal file
1235
dav/SabreDAV/docs/rfc6047.txt
Normal file
File diff suppressed because it is too large
Load diff
3027
dav/SabreDAV/docs/rfc6321.txt
Normal file
3027
dav/SabreDAV/docs/rfc6321.txt
Normal file
File diff suppressed because it is too large
Load diff
4147
dav/SabreDAV/docs/rfc6350.txt
Normal file
4147
dav/SabreDAV/docs/rfc6350.txt
Normal file
File diff suppressed because it is too large
Load diff
1235
dav/SabreDAV/docs/rfc6351.txt
Normal file
1235
dav/SabreDAV/docs/rfc6351.txt
Normal file
File diff suppressed because it is too large
Load diff
2691
dav/SabreDAV/docs/rfc6352.txt
Normal file
2691
dav/SabreDAV/docs/rfc6352.txt
Normal file
File diff suppressed because it is too large
Load diff
56
dav/SabreDAV/examples/addressbookserver.php
Normal file
56
dav/SabreDAV/examples/addressbookserver.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Addressbook/CardDAV server example
|
||||
|
||||
This server features CardDAV support
|
||||
|
||||
*/
|
||||
|
||||
// settings
|
||||
date_default_timezone_set('Canada/Eastern');
|
||||
|
||||
// Make sure this setting is turned on and reflect the root url for your WebDAV server.
|
||||
// This can be for example the root / or a complete path to your server script
|
||||
$baseUri = '/';
|
||||
|
||||
/* Database */
|
||||
$pdo = new PDO('sqlite:data/db.sqlite');
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
//Mapping PHP errors to exceptions
|
||||
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
|
||||
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
||||
}
|
||||
set_error_handler("exception_error_handler");
|
||||
|
||||
// Autoloader
|
||||
require_once 'lib/Sabre/autoload.php';
|
||||
|
||||
// Backends
|
||||
$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
|
||||
$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
|
||||
$carddavBackend = new Sabre_CardDAV_Backend_PDO($pdo);
|
||||
//$caldavBackend = new Sabre_CalDAV_Backend_PDO($pdo);
|
||||
|
||||
// Setting up the directory tree //
|
||||
$nodes = array(
|
||||
new Sabre_DAVACL_PrincipalCollection($principalBackend),
|
||||
// new Sabre_CalDAV_CalendarRootNode($authBackend, $caldavBackend),
|
||||
new Sabre_CardDAV_AddressBookRoot($principalBackend, $carddavBackend),
|
||||
);
|
||||
|
||||
// The object tree needs in turn to be passed to the server class
|
||||
$server = new Sabre_DAV_Server($nodes);
|
||||
$server->setBaseUri($baseUri);
|
||||
|
||||
// Plugins
|
||||
$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV'));
|
||||
$server->addPlugin(new Sabre_DAV_Browser_Plugin());
|
||||
//$server->addPlugin(new Sabre_CalDAV_Plugin());
|
||||
$server->addPlugin(new Sabre_CardDAV_Plugin());
|
||||
$server->addPlugin(new Sabre_DAVACL_Plugin());
|
||||
|
||||
// And off we go!
|
||||
$server->exec();
|
26
dav/SabreDAV/examples/basicauth.php
Normal file
26
dav/SabreDAV/examples/basicauth.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
// !!!! Make sure the Sabre directory is in the include_path !!!
|
||||
// example:
|
||||
// set_include_path('lib/' . PATH_SEPARATOR . get_include_path());
|
||||
|
||||
// settings
|
||||
date_default_timezone_set('Canada/Eastern');
|
||||
|
||||
// Files we need
|
||||
require_once 'Sabre/autoload.php';
|
||||
|
||||
$u = 'admin';
|
||||
$p = '1234';
|
||||
|
||||
$auth = new Sabre_HTTP_BasicAuth();
|
||||
|
||||
$result = $auth->getUserPass();
|
||||
|
||||
if (!$result || $result[0]!=$u || $result[1]!=$p) {
|
||||
|
||||
$auth->requireLogin();
|
||||
echo "Authentication required\n";
|
||||
die();
|
||||
|
||||
}
|
62
dav/SabreDAV/examples/calendarserver.php
Normal file
62
dav/SabreDAV/examples/calendarserver.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|
||||
CalendarServer example
|
||||
|
||||
This server features CalDAV support
|
||||
|
||||
*/
|
||||
|
||||
// settings
|
||||
date_default_timezone_set('Canada/Eastern');
|
||||
|
||||
// If you want to run the SabreDAV server in a custom location (using mod_rewrite for instance)
|
||||
// You can override the baseUri here.
|
||||
// $baseUri = '/';
|
||||
|
||||
/* Database */
|
||||
$pdo = new PDO('sqlite:data/db.sqlite');
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
//Mapping PHP errors to exceptions
|
||||
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
|
||||
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
||||
}
|
||||
set_error_handler("exception_error_handler");
|
||||
|
||||
// Files we need
|
||||
require_once 'lib/Sabre/autoload.php';
|
||||
|
||||
// Backends
|
||||
$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
|
||||
$calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo);
|
||||
$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
|
||||
|
||||
// Directory structure
|
||||
$tree = array(
|
||||
new Sabre_CalDAV_Principal_Collection($principalBackend),
|
||||
new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend),
|
||||
);
|
||||
|
||||
$server = new Sabre_DAV_Server($tree);
|
||||
|
||||
if (isset($baseUri))
|
||||
$server->setBaseUri($baseUri);
|
||||
|
||||
/* Server Plugins */
|
||||
$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV');
|
||||
$server->addPlugin($authPlugin);
|
||||
|
||||
$aclPlugin = new Sabre_DAVACL_Plugin();
|
||||
$server->addPlugin($aclPlugin);
|
||||
|
||||
$caldavPlugin = new Sabre_CalDAV_Plugin();
|
||||
$server->addPlugin($caldavPlugin);
|
||||
|
||||
// Support for html frontend
|
||||
$browser = new Sabre_DAV_Browser_Plugin();
|
||||
$server->addPlugin($browser);
|
||||
|
||||
// And off we go!
|
||||
$server->exec();
|
25
dav/SabreDAV/examples/digestauth.php
Normal file
25
dav/SabreDAV/examples/digestauth.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
// !!!! Make sure the Sabre directory is in the include_path !!!
|
||||
// example:
|
||||
// set_include_path('lib/' . PATH_SEPARATOR . get_include_path());
|
||||
|
||||
// settings
|
||||
date_default_timezone_set('Canada/Eastern');
|
||||
|
||||
// Files we need
|
||||
require_once 'Sabre/autoload.php';
|
||||
|
||||
$u = 'admin';
|
||||
$p = '1234';
|
||||
|
||||
$auth = new Sabre_HTTP_DigestAuth();
|
||||
$auth->init();
|
||||
|
||||
if ($auth->getUsername() != $u || !$auth->validatePassword($p)) {
|
||||
|
||||
$auth->requireLogin();
|
||||
echo "Authentication required\n";
|
||||
die();
|
||||
|
||||
}
|
60
dav/SabreDAV/examples/fileserver.php
Normal file
60
dav/SabreDAV/examples/fileserver.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
// !!!! Make sure the Sabre directory is in the include_path !!!
|
||||
// example:
|
||||
set_include_path('lib/' . PATH_SEPARATOR . get_include_path());
|
||||
|
||||
/*
|
||||
|
||||
This is the best starting point if you're just interested in setting up a fileserver.
|
||||
|
||||
Make sure that the 'public' and 'tmpdata' exists, with write permissions
|
||||
for your server.
|
||||
|
||||
*/
|
||||
|
||||
// settings
|
||||
date_default_timezone_set('Canada/Eastern');
|
||||
$publicDir = 'public';
|
||||
$tmpDir = 'tmpdata';
|
||||
|
||||
// If you want to run the SabreDAV server in a custom location (using mod_rewrite for instance)
|
||||
// You can override the baseUri here.
|
||||
// $baseUri = '/';
|
||||
|
||||
|
||||
// Files we need
|
||||
require_once 'Sabre/autoload.php';
|
||||
|
||||
// Create the root node
|
||||
$root = new Sabre_DAV_FS_Directory($publicDir);
|
||||
|
||||
// The rootnode needs in turn to be passed to the server class
|
||||
$server = new Sabre_DAV_Server($root);
|
||||
|
||||
if (isset($baseUri))
|
||||
$server->setBaseUri($baseUri);
|
||||
|
||||
// Support for LOCK and UNLOCK
|
||||
$lockBackend = new Sabre_DAV_Locks_Backend_File($tmpDir . '/locksdb');
|
||||
$lockPlugin = new Sabre_DAV_Locks_Plugin($lockBackend);
|
||||
$server->addPlugin($lockPlugin);
|
||||
|
||||
// Support for html frontend
|
||||
$browser = new Sabre_DAV_Browser_Plugin();
|
||||
$server->addPlugin($browser);
|
||||
|
||||
// Automatically guess (some) contenttypes, based on extesion
|
||||
$server->addPlugin(new Sabre_DAV_Browser_GuessContentType());
|
||||
|
||||
// Authentication backend
|
||||
$authBackend = new Sabre_DAV_Auth_Backend_File('.htdigest');
|
||||
$auth = new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV');
|
||||
$server->addPlugin($auth);
|
||||
|
||||
// Temporary file filter
|
||||
$tempFF = new Sabre_DAV_TemporaryFileFilterPlugin($tmpDir);
|
||||
$server->addPlugin($tempFF);
|
||||
|
||||
// And off we go!
|
||||
$server->exec();
|
91
dav/SabreDAV/examples/groupwareserver.php
Normal file
91
dav/SabreDAV/examples/groupwareserver.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This server combines both CardDAV and CalDAV functionality into a single
|
||||
* server. It is assumed that the server runs at the root of a HTTP domain (be
|
||||
* that a domainname-based vhost or a specific TCP port.
|
||||
*
|
||||
* This example also assumes that you're using SQLite and the database has
|
||||
* already been setup (along with the database tables).
|
||||
*
|
||||
* You may choose to use MySQL instead, just change the PDO connection
|
||||
* statement.
|
||||
*/
|
||||
|
||||
/**
|
||||
* UTC or GMT is easy to work with, and usually recommended for any
|
||||
* application.
|
||||
*/
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
/**
|
||||
* Make sure this setting is turned on and reflect the root url for your WebDAV
|
||||
* server.
|
||||
*
|
||||
* This can be for example the root / or a complete path to your server script.
|
||||
*/
|
||||
$baseUri = '/';
|
||||
|
||||
/**
|
||||
* Database
|
||||
*
|
||||
* Feel free to switch this to MySQL, it will definitely be better for higher
|
||||
* concurrency.
|
||||
*/
|
||||
$pdo = new PDO('sqlite:data/db.sqlite');
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
/**
|
||||
* Mapping PHP errors to exceptions.
|
||||
*
|
||||
* While this is not strictly needed, it makes a lot of sense to do so. If an
|
||||
* E_NOTICE or anything appears in your code, this allows SabreDAV to intercept
|
||||
* the issue and send a proper response back to the client (HTTP/1.1 500).
|
||||
*/
|
||||
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
|
||||
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
||||
}
|
||||
set_error_handler("exception_error_handler");
|
||||
|
||||
// Autoloader
|
||||
require_once 'lib/Sabre/autoload.php';
|
||||
|
||||
/**
|
||||
* The backends. Yes we do really need all of them.
|
||||
*
|
||||
* This allows any developer to subclass just any of them and hook into their
|
||||
* own backend systems.
|
||||
*/
|
||||
$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
|
||||
$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
|
||||
$carddavBackend = new Sabre_CardDAV_Backend_PDO($pdo);
|
||||
$caldavBackend = new Sabre_CalDAV_Backend_PDO($pdo);
|
||||
|
||||
/**
|
||||
* The directory tree
|
||||
*
|
||||
* Basically this is an array which contains the 'top-level' directories in the
|
||||
* WebDAV server.
|
||||
*/
|
||||
$nodes = array(
|
||||
// /principals
|
||||
new Sabre_CalDAV_Principal_Collection($principalBackend),
|
||||
// /calendars
|
||||
new Sabre_CalDAV_CalendarRootNode($principalBackend, $caldavBackend),
|
||||
// /addressbook
|
||||
new Sabre_CardDAV_AddressBookRoot($principalBackend, $carddavBackend),
|
||||
);
|
||||
|
||||
// The object tree needs in turn to be passed to the server class
|
||||
$server = new Sabre_DAV_Server($nodes);
|
||||
$server->setBaseUri($baseUri);
|
||||
|
||||
// Plugins
|
||||
$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV'));
|
||||
$server->addPlugin(new Sabre_DAV_Browser_Plugin());
|
||||
$server->addPlugin(new Sabre_CalDAV_Plugin());
|
||||
$server->addPlugin(new Sabre_CardDAV_Plugin());
|
||||
$server->addPlugin(new Sabre_DAVACL_Plugin());
|
||||
|
||||
// And off we go!
|
||||
$server->exec();
|
123
dav/SabreDAV/examples/simplefsserver.php
Normal file
123
dav/SabreDAV/examples/simplefsserver.php
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
// !!!! Make sure the Sabre directory is in the include_path !!!
|
||||
// example:
|
||||
// set_include_path('lib/' . PATH_SEPARATOR . get_include_path());
|
||||
|
||||
/*
|
||||
|
||||
This example demonstrates a simple way to create your own virtual filesystems.
|
||||
By extending the _File and Directory classes, you can easily create a tree
|
||||
based on various datasources.
|
||||
|
||||
The most obvious example is the filesystem itself. A more complete and documented
|
||||
example can be found in:
|
||||
|
||||
lib/Sabre/DAV/FS/Node.php
|
||||
lib/Sabre/DAV/FS/Directory.php
|
||||
lib/Sabre/DAV/FS/File.php
|
||||
|
||||
*/
|
||||
|
||||
// settings
|
||||
date_default_timezone_set('Canada/Eastern');
|
||||
$publicDir = 'public';
|
||||
|
||||
// Files we need
|
||||
require_once 'Sabre/autoload.php';
|
||||
|
||||
class MyDirectory extends Sabre_DAV_Directory {
|
||||
|
||||
private $myPath;
|
||||
|
||||
function __construct($myPath) {
|
||||
|
||||
$this->myPath = $myPath;
|
||||
|
||||
}
|
||||
|
||||
function getChildren() {
|
||||
|
||||
$children = array();
|
||||
// Loop through the directory, and create objects for each node
|
||||
foreach(scandir($this->myPath) as $node) {
|
||||
|
||||
// Ignoring files staring with .
|
||||
if ($node[0]==='.') continue;
|
||||
|
||||
$children[] = $this->getChild($node);
|
||||
|
||||
}
|
||||
|
||||
return $children;
|
||||
|
||||
}
|
||||
|
||||
function getChild($name) {
|
||||
|
||||
$path = $this->myPath . '/' . $name;
|
||||
|
||||
// We have to throw a NotFound exception if the file didn't exist
|
||||
if (!file_exists($this->myPath)) throw new Sabre_DAV_Exception_NotFound('The file with name: ' . $name . ' could not be found');
|
||||
// Some added security
|
||||
|
||||
if ($name[0]=='.') throw new Sabre_DAV_Exception_NotFound('Access denied');
|
||||
|
||||
if (is_dir($path)) {
|
||||
|
||||
return new MyDirectory($name);
|
||||
|
||||
} else {
|
||||
|
||||
return new MyFile($path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getName() {
|
||||
|
||||
return basename($this->myPath);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MyFile extends Sabre_DAV_File {
|
||||
|
||||
private $myPath;
|
||||
|
||||
function __construct($myPath) {
|
||||
|
||||
$this->myPath = $myPath;
|
||||
|
||||
}
|
||||
|
||||
function getName() {
|
||||
|
||||
return basename($this->myPath);
|
||||
|
||||
}
|
||||
|
||||
function get() {
|
||||
|
||||
return fopen($this->myPath,'r');
|
||||
|
||||
}
|
||||
|
||||
function getSize() {
|
||||
|
||||
return filesize($this->myPath);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Make sure there is a directory in your current directory named 'public'. We will be exposing that directory to WebDAV
|
||||
$rootNode = new MyDirectory($publicDir);
|
||||
|
||||
// The rootNode needs to be passed to the server object.
|
||||
$server = new Sabre_DAV_Server($rootNode);
|
||||
|
||||
// And off we go!
|
||||
$server->exec();
|
18
dav/SabreDAV/examples/sql/mysql.addressbook.sql
Normal file
18
dav/SabreDAV/examples/sql/mysql.addressbook.sql
Normal file
|
@ -0,0 +1,18 @@
|
|||
CREATE TABLE addressbooks (
|
||||
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
principaluri VARCHAR(255),
|
||||
displayname VARCHAR(255),
|
||||
uri VARCHAR(200),
|
||||
description TEXT,
|
||||
ctag INT(11) UNSIGNED NOT NULL DEFAULT '1',
|
||||
UNIQUE(principaluri, uri)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
CREATE TABLE cards (
|
||||
id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
addressbookid INT(11) UNSIGNED NOT NULL,
|
||||
carddata MEDIUMBLOB,
|
||||
uri VARCHAR(200),
|
||||
lastmodified INT(11) UNSIGNED
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
27
dav/SabreDAV/examples/sql/mysql.calendars.sql
Normal file
27
dav/SabreDAV/examples/sql/mysql.calendars.sql
Normal file
|
@ -0,0 +1,27 @@
|
|||
CREATE TABLE calendarobjects (
|
||||
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
calendardata MEDIUMBLOB,
|
||||
uri VARCHAR(200),
|
||||
calendarid INTEGER UNSIGNED NOT NULL,
|
||||
lastmodified INT(11) UNSIGNED,
|
||||
etag VARCHAR(32),
|
||||
size INT(11) UNSIGNED NOT NULL,
|
||||
componenttype VARCHAR(8),
|
||||
firstoccurence INT(11) UNSIGNED,
|
||||
lastoccurence INT(11) UNSIGNED,
|
||||
UNIQUE(calendarid, uri)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
CREATE TABLE calendars (
|
||||
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
principaluri VARCHAR(100),
|
||||
displayname VARCHAR(100),
|
||||
uri VARCHAR(200),
|
||||
ctag INTEGER UNSIGNED NOT NULL DEFAULT '0',
|
||||
description TEXT,
|
||||
calendarorder INTEGER UNSIGNED NOT NULL DEFAULT '0',
|
||||
calendarcolor VARCHAR(10),
|
||||
timezone TEXT,
|
||||
components VARCHAR(20),
|
||||
UNIQUE(principaluri, uri)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
10
dav/SabreDAV/examples/sql/mysql.locks.sql
Normal file
10
dav/SabreDAV/examples/sql/mysql.locks.sql
Normal file
|
@ -0,0 +1,10 @@
|
|||
CREATE TABLE locks (
|
||||
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
owner VARCHAR(100),
|
||||
timeout INTEGER UNSIGNED,
|
||||
created INTEGER,
|
||||
token VARCHAR(100),
|
||||
scope TINYINT,
|
||||
depth TINYINT,
|
||||
uri text
|
||||
);
|
22
dav/SabreDAV/examples/sql/mysql.principals.sql
Normal file
22
dav/SabreDAV/examples/sql/mysql.principals.sql
Normal file
|
@ -0,0 +1,22 @@
|
|||
CREATE TABLE principals (
|
||||
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
uri VARCHAR(200) NOT NULL,
|
||||
email VARCHAR(80),
|
||||
displayname VARCHAR(80),
|
||||
vcardurl VARCHAR(80),
|
||||
UNIQUE(uri)
|
||||
);
|
||||
|
||||
CREATE TABLE groupmembers (
|
||||
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
principal_id INTEGER UNSIGNED NOT NULL,
|
||||
member_id INTEGER UNSIGNED NOT NULL,
|
||||
UNIQUE(principal_id, member_id)
|
||||
);
|
||||
|
||||
|
||||
INSERT INTO principals (uri,email,displayname) VALUES
|
||||
('principals/admin', 'admin@example.org','Administrator'),
|
||||
('principals/admin/calendar-proxy-read', null, null),
|
||||
('principals/admin/calendar-proxy-write', null, null);
|
||||
|
9
dav/SabreDAV/examples/sql/mysql.users.sql
Normal file
9
dav/SabreDAV/examples/sql/mysql.users.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE users (
|
||||
id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
username VARCHAR(50),
|
||||
digesta1 VARCHAR(32),
|
||||
UNIQUE(username)
|
||||
);
|
||||
|
||||
INSERT INTO users (username,digesta1) VALUES
|
||||
('admin', '87fd274b7b6c01e48d7c2f965da8ddf7');
|
33
dav/SabreDAV/examples/sql/pgsql.addressbook.sql
Normal file
33
dav/SabreDAV/examples/sql/pgsql.addressbook.sql
Normal file
|
@ -0,0 +1,33 @@
|
|||
CREATE TABLE addressbooks (
|
||||
id SERIAL NOT NULL,
|
||||
principaluri VARCHAR(255),
|
||||
displayname VARCHAR(255),
|
||||
uri VARCHAR(200),
|
||||
description TEXT,
|
||||
ctag INTEGER NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY addressbooks
|
||||
ADD CONSTRAINT addressbooks_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX addressbooks_ukey
|
||||
ON addressbooks USING btree (principaluri, uri);
|
||||
|
||||
CREATE TABLE cards (
|
||||
id SERIAL NOT NULL,
|
||||
addressbookid INTEGER NOT NULL,
|
||||
carddata TEXT,
|
||||
uri VARCHAR(200),
|
||||
lastmodified INTEGER
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY cards
|
||||
ADD CONSTRAINT cards_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX cards_ukey
|
||||
ON cards USING btree (addressbookid, uri);
|
||||
|
||||
ALTER TABLE ONLY cards
|
||||
ADD CONSTRAINT cards_addressbookid_fkey FOREIGN KEY (addressbookid) REFERENCES addressbooks(id)
|
||||
ON DELETE CASCADE;
|
||||
|
41
dav/SabreDAV/examples/sql/pgsql.calendars.sql
Normal file
41
dav/SabreDAV/examples/sql/pgsql.calendars.sql
Normal file
|
@ -0,0 +1,41 @@
|
|||
CREATE TABLE calendars (
|
||||
id SERIAL NOT NULL,
|
||||
principaluri VARCHAR(100),
|
||||
displayname VARCHAR(100),
|
||||
uri VARCHAR(200),
|
||||
ctag INTEGER NOT NULL DEFAULT 0,
|
||||
description TEXT,
|
||||
calendarorder INTEGER NOT NULL DEFAULT 0,
|
||||
calendarcolor VARCHAR(10),
|
||||
timezone TEXT,
|
||||
components VARCHAR(20)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendars
|
||||
ADD CONSTRAINT calendars_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX calendars_ukey
|
||||
ON calendars USING btree (principaluri, uri);
|
||||
|
||||
CREATE TABLE calendarobjects (
|
||||
id SERIAL NOT NULL,
|
||||
calendarid INTEGER NOT NULL,
|
||||
calendardata TEXT,
|
||||
uri VARCHAR(200),
|
||||
etag VARCHAR(32),
|
||||
size INTEGER NOT NULL,
|
||||
componenttype VARCHAR(8),
|
||||
lastmodified INTEGER
|
||||
firstoccurence INTEGER,
|
||||
lastoccurence INTEGER
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY calendarobjects
|
||||
ADD CONSTRAINT calendarobjects_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX calendarobjects_ukey
|
||||
ON calendarobjects USING btree (calendarid, uri);
|
||||
|
||||
ALTER TABLE ONLY calendarobjects
|
||||
ADD CONSTRAINT calendarobjects_calendarid_fkey FOREIGN KEY (calendarid) REFERENCES calendars(id)
|
||||
ON DELETE CASCADE;
|
13
dav/SabreDAV/examples/sql/pgsql.locks.sql
Normal file
13
dav/SabreDAV/examples/sql/pgsql.locks.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
CREATE TABLE locks (
|
||||
id SERIAL NOT NULL,
|
||||
owner VARCHAR(100),
|
||||
timeout INTEGER,
|
||||
created INTEGER,
|
||||
token VARCHAR(100),
|
||||
scope smallint,
|
||||
depth smallint,
|
||||
uri text
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY locks
|
||||
ADD CONSTRAINT locks_pkey PRIMARY KEY (id);
|
40
dav/SabreDAV/examples/sql/pgsql.principals.sql
Normal file
40
dav/SabreDAV/examples/sql/pgsql.principals.sql
Normal file
|
@ -0,0 +1,40 @@
|
|||
CREATE TABLE principals (
|
||||
id SERIAL NOT NULL,
|
||||
uri VARCHAR(100) NOT NULL,
|
||||
email VARCHAR(80),
|
||||
displayname VARCHAR(80),
|
||||
vcardurl VARCHAR(80)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY principals
|
||||
ADD CONSTRAINT principals_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX principals_ukey
|
||||
ON principals USING btree (uri);
|
||||
|
||||
CREATE TABLE groupmembers (
|
||||
id SERIAL NOT NULL,
|
||||
principal_id INTEGER NOT NULL,
|
||||
member_id INTEGER NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY groupmembers
|
||||
ADD CONSTRAINT groupmembers_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX groupmembers_ukey
|
||||
ON groupmembers USING btree (principal_id, member_id);
|
||||
|
||||
ALTER TABLE ONLY groupmembers
|
||||
ADD CONSTRAINT groupmembers_principal_id_fkey FOREIGN KEY (principal_id) REFERENCES principals(id)
|
||||
ON DELETE CASCADE;
|
||||
|
||||
-- Is this correct correct link ... or not?
|
||||
-- ALTER TABLE ONLY groupmembers
|
||||
-- ADD CONSTRAINT groupmembers_member_id_id_fkey FOREIGN KEY (member_id) REFERENCES users(id)
|
||||
-- ON DELETE CASCADE;
|
||||
|
||||
INSERT INTO principals (uri,email,displayname) VALUES
|
||||
('principals/admin', 'admin@example.org','Administrator'),
|
||||
('principals/admin/calendar-proxy-read', null, null),
|
||||
('principals/admin/calendar-proxy-write', null, null);
|
||||
|
15
dav/SabreDAV/examples/sql/pgsql.users.sql
Normal file
15
dav/SabreDAV/examples/sql/pgsql.users.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE users (
|
||||
id SERIAL NOT NULL,
|
||||
username VARCHAR(50),
|
||||
digesta1 VARCHAR(32),
|
||||
UNIQUE(username)
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY users
|
||||
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||
|
||||
CREATE UNIQUE INDEX users_ukey
|
||||
ON users USING btree (username);
|
||||
|
||||
INSERT INTO users (username,digesta1) VALUES
|
||||
('admin', '87fd274b7b6c01e48d7c2f965da8ddf7');
|
17
dav/SabreDAV/examples/sql/sqlite.addressbooks.sql
Normal file
17
dav/SabreDAV/examples/sql/sqlite.addressbooks.sql
Normal file
|
@ -0,0 +1,17 @@
|
|||
CREATE TABLE addressbooks (
|
||||
id integer primary key asc,
|
||||
principaluri text,
|
||||
displayname text,
|
||||
uri text,
|
||||
description text,
|
||||
ctag integer
|
||||
);
|
||||
|
||||
CREATE TABLE cards (
|
||||
id integer primary key asc,
|
||||
addressbookid integer,
|
||||
carddata blob,
|
||||
uri text,
|
||||
lastmodified integer
|
||||
);
|
||||
|
25
dav/SabreDAV/examples/sql/sqlite.calendars.sql
Normal file
25
dav/SabreDAV/examples/sql/sqlite.calendars.sql
Normal file
|
@ -0,0 +1,25 @@
|
|||
CREATE TABLE calendarobjects (
|
||||
id integer primary key asc,
|
||||
calendardata blob,
|
||||
uri text,
|
||||
calendarid integer,
|
||||
lastmodified integer,
|
||||
etag text,
|
||||
size integer,
|
||||
componenttype text,
|
||||
firstoccurence integer,
|
||||
lastoccurence integer
|
||||
);
|
||||
|
||||
CREATE TABLE calendars (
|
||||
id integer primary key asc,
|
||||
principaluri text,
|
||||
displayname text,
|
||||
uri text,
|
||||
ctag integer,
|
||||
description text,
|
||||
calendarorder integer,
|
||||
calendarcolor text,
|
||||
timezone text,
|
||||
components text
|
||||
);
|
12
dav/SabreDAV/examples/sql/sqlite.locks.sql
Normal file
12
dav/SabreDAV/examples/sql/sqlite.locks.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
BEGIN TRANSACTION;
|
||||
CREATE TABLE locks (
|
||||
id integer primary key asc,
|
||||
owner text,
|
||||
timeout integer,
|
||||
created integer,
|
||||
token text,
|
||||
scope integer,
|
||||
depth integer,
|
||||
uri text
|
||||
);
|
||||
COMMIT;
|
21
dav/SabreDAV/examples/sql/sqlite.principals.sql
Normal file
21
dav/SabreDAV/examples/sql/sqlite.principals.sql
Normal file
|
@ -0,0 +1,21 @@
|
|||
CREATE TABLE principals (
|
||||
id INTEGER PRIMARY KEY ASC,
|
||||
uri TEXT,
|
||||
email TEXT,
|
||||
displayname TEXT,
|
||||
vcardurl TEXT,
|
||||
UNIQUE(uri)
|
||||
);
|
||||
|
||||
CREATE TABLE groupmembers (
|
||||
id INTEGER PRIMARY KEY ASC,
|
||||
principal_id INTEGER,
|
||||
member_id INTEGER,
|
||||
UNIQUE(principal_id, member_id)
|
||||
);
|
||||
|
||||
|
||||
INSERT INTO principals (uri,email,displayname) VALUES ('principals/admin', 'admin@example.org','Administrator');
|
||||
INSERT INTO principals (uri,email,displayname) VALUES ('principals/admin/calendar-proxy-read', null, null);
|
||||
INSERT INTO principals (uri,email,displayname) VALUES ('principals/admin/calendar-proxy-write', null, null);
|
||||
|
9
dav/SabreDAV/examples/sql/sqlite.users.sql
Normal file
9
dav/SabreDAV/examples/sql/sqlite.users.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE users (
|
||||
id integer primary key asc,
|
||||
username TEXT,
|
||||
digesta1 TEXT,
|
||||
UNIQUE(username)
|
||||
);
|
||||
|
||||
INSERT INTO users (username,digesta1) VALUES
|
||||
('admin', '87fd274b7b6c01e48d7c2f965da8ddf7');
|
16
dav/SabreDAV/examples/webserver/apache2_htaccess.conf
Normal file
16
dav/SabreDAV/examples/webserver/apache2_htaccess.conf
Normal file
|
@ -0,0 +1,16 @@
|
|||
RewriteEngine On
|
||||
# This makes every request go to server.php
|
||||
RewriteRule (.*) server.php [L]
|
||||
|
||||
# Output buffering needs to be off, to prevent high memory usage
|
||||
php_flag output_buffering off
|
||||
|
||||
# This is also to prevent high memory usage
|
||||
php_flag always_populate_raw_post_data off
|
||||
|
||||
# This is almost a given, but magic quotes is *still* on on some
|
||||
# linux distributions
|
||||
php_flag magic_quotes_gpc off
|
||||
|
||||
# SabreDAV is not compatible with mbstring function overloading
|
||||
php_flag mbstring.func_overload off
|
33
dav/SabreDAV/examples/webserver/apache2_vhost.conf
Normal file
33
dav/SabreDAV/examples/webserver/apache2_vhost.conf
Normal file
|
@ -0,0 +1,33 @@
|
|||
# This is a sample configuration to setup a dedicated Apache vhost for WebDAV.
|
||||
#
|
||||
# The main thing that should be configured is the servername, and the path to
|
||||
# your SabreDAV installation (DocumentRoot).
|
||||
#
|
||||
# This configuration assumed mod_php5 is used, as it sets a few default php
|
||||
# settings as well.
|
||||
<VirtualHost *:*>
|
||||
|
||||
# Don't forget to change the server name
|
||||
# ServerName dav.example.org
|
||||
|
||||
# The DocumentRoot is also required
|
||||
# DocumentRoot /home/sabredav/
|
||||
|
||||
RewriteEngine On
|
||||
# This makes every request go to server.php
|
||||
RewriteRule ^/(.*)$ /server.php [L]
|
||||
|
||||
# Output buffering needs to be off, to prevent high memory usage
|
||||
php_flag output_buffering off
|
||||
|
||||
# This is also to prevent high memory usage
|
||||
php_flag always_populate_raw_post_data off
|
||||
|
||||
# This is almost a given, but magic quotes is *still* on on some
|
||||
# linux distributions
|
||||
php_flag magic_quotes_gpc off
|
||||
|
||||
# SabreDAV is not compatible with mbstring function overloading
|
||||
php_flag mbstring.func_overload off
|
||||
|
||||
</VirtualHost *:*>
|
21
dav/SabreDAV/examples/webserver/apache2_vhost_cgi.conf
Normal file
21
dav/SabreDAV/examples/webserver/apache2_vhost_cgi.conf
Normal file
|
@ -0,0 +1,21 @@
|
|||
# This is a sample configuration to setup a dedicated Apache vhost for WebDAV.
|
||||
#
|
||||
# The main thing that should be configured is the servername, and the path to
|
||||
# your SabreDAV installation (DocumentRoot).
|
||||
#
|
||||
# This configuration assumes CGI or FastCGI is used.
|
||||
<VirtualHost *:*>
|
||||
|
||||
# Don't forget to change the server name
|
||||
# ServerName dav.example.org
|
||||
|
||||
# The DocumentRoot is also required
|
||||
# DocumentRoot /home/sabredav/
|
||||
|
||||
# This makes every request go to server.php. This also makes sure
|
||||
# the Authentication information is available. If your server script is
|
||||
# not called server.php, be sure to change it.
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*)$ /server.php [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
</VirtualHost *:*>
|
26
dav/SabreDAV/lib/Sabre.includes.php
Normal file
26
dav/SabreDAV/lib/Sabre.includes.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Library include file
|
||||
*
|
||||
* This file is deprecated, don't use it!
|
||||
* Instead, use the specific includes files that are in the sub-packages.
|
||||
*
|
||||
* Sabre/DAV/includes.php
|
||||
* Sabre/HTTP/includes.php
|
||||
*
|
||||
* etc..
|
||||
*
|
||||
* This file contains all includes to the rest of the SabreDAV library
|
||||
* Make sure the lib/ directory is in PHP's include_path.
|
||||
*
|
||||
* @package Sabre
|
||||
* @deprecated Don't use this file, it will be remove in a future version
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
include 'Sabre/HTTP/includes.php';
|
||||
include 'Sabre/DAV/includes.php';
|
||||
|
277
dav/SabreDAV/lib/Sabre/CalDAV/Backend/Abstract.php
Normal file
277
dav/SabreDAV/lib/Sabre/CalDAV/Backend/Abstract.php
Normal file
|
@ -0,0 +1,277 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Abstract Calendaring backend. Extend this class to create your own backends.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_CalDAV_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* Returns a list of calendars for a principal.
|
||||
*
|
||||
* Every project is an array with the following keys:
|
||||
* * id, a unique id that will be used by other functions to modify the
|
||||
* calendar. This can be the same as the uri or a database key.
|
||||
* * uri, which the basename of the uri with which the calendar is
|
||||
* accessed.
|
||||
* * principaluri. The owner of the calendar. Almost always the same as
|
||||
* principalUri passed to this method.
|
||||
*
|
||||
* Furthermore it can contain webdav properties in clark notation. A very
|
||||
* common one is '{DAV:}displayname'.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
abstract function getCalendarsForUser($principalUri);
|
||||
|
||||
/**
|
||||
* Creates a new calendar for a principal.
|
||||
*
|
||||
* If the creation was a success, an id must be returned that can be used to reference
|
||||
* this calendar in other methods, such as updateCalendar.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $calendarUri
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
abstract function createCalendar($principalUri,$calendarUri,array $properties);
|
||||
|
||||
/**
|
||||
* Updates properties for a calendar.
|
||||
*
|
||||
* The mutations array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existent property is always successful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param array $mutations
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateCalendar($calendarId, array $mutations) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar and all it's objects
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @return void
|
||||
*/
|
||||
abstract function deleteCalendar($calendarId);
|
||||
|
||||
/**
|
||||
* Returns all calendar objects within a calendar.
|
||||
*
|
||||
* Every item contains an array with the following keys:
|
||||
* * id - unique identifier which will be used for subsequent updates
|
||||
* * calendardata - The iCalendar-compatible calendar data
|
||||
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
|
||||
* * lastmodified - a timestamp of the last modification time
|
||||
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||
* ' "abcdef"')
|
||||
* * calendarid - The calendarid as it was passed to this function.
|
||||
* * size - The size of the calendar objects, in bytes.
|
||||
*
|
||||
* Note that the etag is optional, but it's highly encouraged to return for
|
||||
* speed reasons.
|
||||
*
|
||||
* The calendardata is also optional. If it's not returned
|
||||
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||
* calendardata.
|
||||
*
|
||||
* If neither etag or size are specified, the calendardata will be
|
||||
* used/fetched to determine these numbers. If both are specified the
|
||||
* amount of times this is needed is reduced by a great degree.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @return array
|
||||
*/
|
||||
abstract function getCalendarObjects($calendarId);
|
||||
|
||||
/**
|
||||
* Returns information from a single calendar object, based on it's object
|
||||
* uri.
|
||||
*
|
||||
* The returned array must have the same keys as getCalendarObjects. The
|
||||
* 'calendardata' object is required here though, while it's not required
|
||||
* for getCalendarObjects.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @return array
|
||||
*/
|
||||
abstract function getCalendarObject($calendarId,$objectUri);
|
||||
|
||||
/**
|
||||
* Creates a new calendar object.
|
||||
*
|
||||
* It is possible return an etag from this function, which will be used in
|
||||
* the response to this PUT request. Note that the ETag must be surrounded
|
||||
* by double-quotes.
|
||||
*
|
||||
* However, you should only really return this ETag if you don't mangle the
|
||||
* calendar-data. If the result of a subsequent GET to this object is not
|
||||
* the exact same as this request body, you should omit the ETag.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return string|null
|
||||
*/
|
||||
abstract function createCalendarObject($calendarId,$objectUri,$calendarData);
|
||||
|
||||
/**
|
||||
* Updates an existing calendarobject, based on it's uri.
|
||||
*
|
||||
* It is possible return an etag from this function, which will be used in
|
||||
* the response to this PUT request. Note that the ETag must be surrounded
|
||||
* by double-quotes.
|
||||
*
|
||||
* However, you should only really return this ETag if you don't mangle the
|
||||
* calendar-data. If the result of a subsequent GET to this object is not
|
||||
* the exact same as this request body, you should omit the ETag.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return string|null
|
||||
*/
|
||||
abstract function updateCalendarObject($calendarId,$objectUri,$calendarData);
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
abstract function deleteCalendarObject($calendarId,$objectUri);
|
||||
|
||||
/**
|
||||
* Performs a calendar-query on the contents of this calendar.
|
||||
*
|
||||
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||
* calendar-query it is possible for a client to request a specific set of
|
||||
* object, based on contents of iCalendar properties, date-ranges and
|
||||
* iCalendar component types (VTODO, VEVENT).
|
||||
*
|
||||
* This method should just return a list of (relative) urls that match this
|
||||
* query.
|
||||
*
|
||||
* The list of filters are specified as an array. The exact array is
|
||||
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||
*
|
||||
* Note that it is extremely likely that getCalendarObject for every path
|
||||
* returned from this method will be called almost immediately after. You
|
||||
* may want to anticipate this to speed up these requests.
|
||||
*
|
||||
* This method provides a default implementation, which parses *all* the
|
||||
* iCalendar objects in the specified calendar.
|
||||
*
|
||||
* This default may well be good enough for personal use, and calendars
|
||||
* that aren't very large. But if you anticipate high usage, big calendars
|
||||
* or high loads, you are strongly adviced to optimize certain paths.
|
||||
*
|
||||
* The best way to do so is override this method and to optimize
|
||||
* specifically for 'common filters'.
|
||||
*
|
||||
* Requests that are extremely common are:
|
||||
* * requests for just VEVENTS
|
||||
* * requests for just VTODO
|
||||
* * requests with a time-range-filter on either VEVENT or VTODO.
|
||||
*
|
||||
* ..and combinations of these requests. It may not be worth it to try to
|
||||
* handle every possible situation and just rely on the (relatively
|
||||
* easy to use) CalendarQueryValidator to handle the rest.
|
||||
*
|
||||
* Note that especially time-range-filters may be difficult to parse. A
|
||||
* time-range filter specified on a VEVENT must for instance also handle
|
||||
* recurrence rules correctly.
|
||||
* A good example of how to interprete all these filters can also simply
|
||||
* be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct
|
||||
* as possible, so it gives you a good idea on what type of stuff you need
|
||||
* to think of.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param array $filters
|
||||
* @return array
|
||||
*/
|
||||
public function calendarQuery($calendarId, array $filters) {
|
||||
|
||||
$result = array();
|
||||
$objects = $this->getCalendarObjects($calendarId);
|
||||
|
||||
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
||||
|
||||
foreach($objects as $object) {
|
||||
|
||||
if ($this->validateFilterForObject($object, $filters)) {
|
||||
$result[] = $object['uri'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates if a filters (as passed to calendarQuery) matches
|
||||
* the given object.
|
||||
*
|
||||
* @param array $object
|
||||
* @param array $filter
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateFilterForObject(array $object, array $filters) {
|
||||
|
||||
// Unfortunately, setting the 'calendardata' here is optional. If
|
||||
// it was excluded, we actually need another call to get this as
|
||||
// well.
|
||||
if (!isset($object['calendardata'])) {
|
||||
$object = $this->getCalendarObject($object['calendarid'], $object['uri']);
|
||||
}
|
||||
|
||||
$vObject = Sabre_VObject_Reader::read($object['calendardata']);
|
||||
|
||||
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
||||
return $validator->validate($vObject, $filters);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
657
dav/SabreDAV/lib/Sabre/CalDAV/Backend/PDO.php
Normal file
657
dav/SabreDAV/lib/Sabre/CalDAV/Backend/PDO.php
Normal file
|
@ -0,0 +1,657 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PDO CalDAV backend
|
||||
*
|
||||
* This backend is used to store calendar-data in a PDO database, such as
|
||||
* sqlite or MySQL
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* We need to specify a max date, because we need to stop *somewhere*
|
||||
*/
|
||||
const MAX_DATE = '2040-01-01';
|
||||
|
||||
/**
|
||||
* pdo
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* The table name that will be used for calendars
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $calendarTableName;
|
||||
|
||||
/**
|
||||
* The table name that will be used for calendar objects
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $calendarObjectTableName;
|
||||
|
||||
/**
|
||||
* List of CalDAV properties, and how they map to database fieldnames
|
||||
*
|
||||
* Add your own properties by simply adding on to this array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $propertyMap = array(
|
||||
'{DAV:}displayname' => 'displayname',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
|
||||
'{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
|
||||
'{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates the backend
|
||||
*
|
||||
* @param PDO $pdo
|
||||
* @param string $calendarTableName
|
||||
* @param string $calendarObjectTableName
|
||||
*/
|
||||
public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') {
|
||||
|
||||
$this->pdo = $pdo;
|
||||
$this->calendarTableName = $calendarTableName;
|
||||
$this->calendarObjectTableName = $calendarObjectTableName;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of calendars for a principal.
|
||||
*
|
||||
* Every project is an array with the following keys:
|
||||
* * id, a unique id that will be used by other functions to modify the
|
||||
* calendar. This can be the same as the uri or a database key.
|
||||
* * uri, which the basename of the uri with which the calendar is
|
||||
* accessed.
|
||||
* * principaluri. The owner of the calendar. Almost always the same as
|
||||
* principalUri passed to this method.
|
||||
*
|
||||
* Furthermore it can contain webdav properties in clark notation. A very
|
||||
* common one is '{DAV:}displayname'.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarsForUser($principalUri) {
|
||||
|
||||
$fields = array_values($this->propertyMap);
|
||||
$fields[] = 'id';
|
||||
$fields[] = 'uri';
|
||||
$fields[] = 'ctag';
|
||||
$fields[] = 'components';
|
||||
$fields[] = 'principaluri';
|
||||
|
||||
// Making fields a comma-delimited list
|
||||
$fields = implode(', ', $fields);
|
||||
$stmt = $this->pdo->prepare("SELECT " . $fields . " FROM ".$this->calendarTableName." WHERE principaluri = ? ORDER BY calendarorder ASC");
|
||||
$stmt->execute(array($principalUri));
|
||||
|
||||
$calendars = array();
|
||||
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
|
||||
$components = array();
|
||||
if ($row['components']) {
|
||||
$components = explode(',',$row['components']);
|
||||
}
|
||||
|
||||
$calendar = array(
|
||||
'id' => $row['id'],
|
||||
'uri' => $row['uri'],
|
||||
'principaluri' => $row['principaluri'],
|
||||
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
|
||||
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
|
||||
);
|
||||
|
||||
|
||||
foreach($this->propertyMap as $xmlName=>$dbName) {
|
||||
$calendar[$xmlName] = $row[$dbName];
|
||||
}
|
||||
|
||||
$calendars[] = $calendar;
|
||||
|
||||
}
|
||||
|
||||
return $calendars;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar for a principal.
|
||||
*
|
||||
* If the creation was a success, an id must be returned that can be used to reference
|
||||
* this calendar in other methods, such as updateCalendar
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $calendarUri
|
||||
* @param array $properties
|
||||
* @return string
|
||||
*/
|
||||
public function createCalendar($principalUri, $calendarUri, array $properties) {
|
||||
|
||||
$fieldNames = array(
|
||||
'principaluri',
|
||||
'uri',
|
||||
'ctag',
|
||||
);
|
||||
$values = array(
|
||||
':principaluri' => $principalUri,
|
||||
':uri' => $calendarUri,
|
||||
':ctag' => 1,
|
||||
);
|
||||
|
||||
// Default value
|
||||
$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
|
||||
$fieldNames[] = 'components';
|
||||
if (!isset($properties[$sccs])) {
|
||||
$values[':components'] = 'VEVENT,VTODO';
|
||||
} else {
|
||||
if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
|
||||
throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
|
||||
}
|
||||
$values[':components'] = implode(',',$properties[$sccs]->getValue());
|
||||
}
|
||||
|
||||
foreach($this->propertyMap as $xmlName=>$dbName) {
|
||||
if (isset($properties[$xmlName])) {
|
||||
|
||||
$values[':' . $dbName] = $properties[$xmlName];
|
||||
$fieldNames[] = $dbName;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarTableName." (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
|
||||
$stmt->execute($values);
|
||||
|
||||
return $this->pdo->lastInsertId();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties for a calendar.
|
||||
*
|
||||
* The mutations array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existent property is always successful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param array $mutations
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateCalendar($calendarId, array $mutations) {
|
||||
|
||||
$newValues = array();
|
||||
$result = array(
|
||||
200 => array(), // Ok
|
||||
403 => array(), // Forbidden
|
||||
424 => array(), // Failed Dependency
|
||||
);
|
||||
|
||||
$hasError = false;
|
||||
|
||||
foreach($mutations as $propertyName=>$propertyValue) {
|
||||
|
||||
// We don't know about this property.
|
||||
if (!isset($this->propertyMap[$propertyName])) {
|
||||
$hasError = true;
|
||||
$result[403][$propertyName] = null;
|
||||
unset($mutations[$propertyName]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldName = $this->propertyMap[$propertyName];
|
||||
$newValues[$fieldName] = $propertyValue;
|
||||
|
||||
}
|
||||
|
||||
// If there were any errors we need to fail the request
|
||||
if ($hasError) {
|
||||
// Properties has the remaining properties
|
||||
foreach($mutations as $propertyName=>$propertyValue) {
|
||||
$result[424][$propertyName] = null;
|
||||
}
|
||||
|
||||
// Removing unused statuscodes for cleanliness
|
||||
foreach($result as $status=>$properties) {
|
||||
if (is_array($properties) && count($properties)===0) unset($result[$status]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
// Success
|
||||
|
||||
// Now we're generating the sql query.
|
||||
$valuesSql = array();
|
||||
foreach($newValues as $fieldName=>$value) {
|
||||
$valuesSql[] = $fieldName . ' = ?';
|
||||
}
|
||||
$valuesSql[] = 'ctag = ctag + 1';
|
||||
|
||||
$stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?");
|
||||
$newValues['id'] = $calendarId;
|
||||
$stmt->execute(array_values($newValues));
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar and all it's objects
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendar($calendarId) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all calendar objects within a calendar.
|
||||
*
|
||||
* Every item contains an array with the following keys:
|
||||
* * id - unique identifier which will be used for subsequent updates
|
||||
* * calendardata - The iCalendar-compatible calendar data
|
||||
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
|
||||
* * lastmodified - a timestamp of the last modification time
|
||||
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||
* ' "abcdef"')
|
||||
* * calendarid - The calendarid as it was passed to this function.
|
||||
* * size - The size of the calendar objects, in bytes.
|
||||
*
|
||||
* Note that the etag is optional, but it's highly encouraged to return for
|
||||
* speed reasons.
|
||||
*
|
||||
* The calendardata is also optional. If it's not returned
|
||||
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||
* calendardata.
|
||||
*
|
||||
* If neither etag or size are specified, the calendardata will be
|
||||
* used/fetched to determine these numbers. If both are specified the
|
||||
* amount of times this is needed is reduced by a great degree.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarObjects($calendarId) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
$result = array();
|
||||
foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
|
||||
$result[] = array(
|
||||
'id' => $row['id'],
|
||||
'uri' => $row['uri'],
|
||||
'lastmodified' => $row['lastmodified'],
|
||||
'etag' => '"' . $row['etag'] . '"',
|
||||
'calendarid' => $row['calendarid'],
|
||||
'size' => (int)$row['size'],
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information from a single calendar object, based on it's object
|
||||
* uri.
|
||||
*
|
||||
* The returned array must have the same keys as getCalendarObjects. The
|
||||
* 'calendardata' object is required here though, while it's not required
|
||||
* for getCalendarObjects.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarObject($calendarId,$objectUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
|
||||
$stmt->execute(array($calendarId, $objectUri));
|
||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
|
||||
if(!$row) return null;
|
||||
|
||||
return array(
|
||||
'id' => $row['id'],
|
||||
'uri' => $row['uri'],
|
||||
'lastmodified' => $row['lastmodified'],
|
||||
'etag' => '"' . $row['etag'] . '"',
|
||||
'calendarid' => $row['calendarid'],
|
||||
'size' => (int)$row['size'],
|
||||
'calendardata' => $row['calendardata'],
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new calendar object.
|
||||
*
|
||||
* It is possible return an etag from this function, which will be used in
|
||||
* the response to this PUT request. Note that the ETag must be surrounded
|
||||
* by double-quotes.
|
||||
*
|
||||
* However, you should only really return this ETag if you don't mangle the
|
||||
* calendar-data. If the result of a subsequent GET to this object is not
|
||||
* the exact same as this request body, you should omit the ETag.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return string|null
|
||||
*/
|
||||
public function createCalendarObject($calendarId,$objectUri,$calendarData) {
|
||||
|
||||
$extraData = $this->getDenormalizedData($calendarData);
|
||||
|
||||
$stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence) VALUES (?,?,?,?,?,?,?,?,?)');
|
||||
$stmt->execute(array(
|
||||
$calendarId,
|
||||
$objectUri,
|
||||
$calendarData,
|
||||
time(),
|
||||
$extraData['etag'],
|
||||
$extraData['size'],
|
||||
$extraData['componentType'],
|
||||
$extraData['firstOccurence'],
|
||||
$extraData['lastOccurence'],
|
||||
));
|
||||
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
return '"' . $extraData['etag'] . '"';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing calendarobject, based on it's uri.
|
||||
*
|
||||
* It is possible return an etag from this function, which will be used in
|
||||
* the response to this PUT request. Note that the ETag must be surrounded
|
||||
* by double-quotes.
|
||||
*
|
||||
* However, you should only really return this ETag if you don't mangle the
|
||||
* calendar-data. If the result of a subsequent GET to this object is not
|
||||
* the exact same as this request body, you should omit the ETag.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return string|null
|
||||
*/
|
||||
public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
|
||||
|
||||
$extraData = $this->getDenormalizedData($calendarData);
|
||||
|
||||
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE calendarid = ? AND uri = ?');
|
||||
$stmt->execute(array($calendarData,time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'] ,$calendarId,$objectUri));
|
||||
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
return '"' . $extraData['etag'] . '"';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses some information from calendar objects, used for optimized
|
||||
* calendar-queries.
|
||||
*
|
||||
* Returns an array with the following keys:
|
||||
* * etag
|
||||
* * size
|
||||
* * componentType
|
||||
* * firstOccurence
|
||||
* * lastOccurence
|
||||
*
|
||||
* @param string $calendarData
|
||||
* @return array
|
||||
*/
|
||||
protected function getDenormalizedData($calendarData) {
|
||||
|
||||
$vObject = Sabre_VObject_Reader::read($calendarData);
|
||||
$componentType = null;
|
||||
$component = null;
|
||||
$firstOccurence = null;
|
||||
$lastOccurence = null;
|
||||
foreach($vObject->getComponents() as $component) {
|
||||
if ($component->name!=='VTIMEZONE') {
|
||||
$componentType = $component->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$componentType) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
|
||||
}
|
||||
if ($componentType === 'VEVENT') {
|
||||
$firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
|
||||
// Finding the last occurence is a bit harder
|
||||
if (!isset($component->RRULE)) {
|
||||
if (isset($component->DTEND)) {
|
||||
$lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
|
||||
} elseif (isset($component->DURATION)) {
|
||||
$endDate = clone $component->DTSTART->getDateTime();
|
||||
$endDate->add(Sabre_VObject_DateTimeParser::parse($component->DURATION->value));
|
||||
$lastOccurence = $endDate->getTimeStamp();
|
||||
} elseif ($component->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) {
|
||||
$endDate = clone $component->DTSTART->getDateTime();
|
||||
$endDate->modify('+1 day');
|
||||
$lastOccurence = $endDate->getTimeStamp();
|
||||
} else {
|
||||
$lastOccurence = $firstOccurence;
|
||||
}
|
||||
} else {
|
||||
$it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID);
|
||||
$maxDate = new DateTime(self::MAX_DATE);
|
||||
if ($it->isInfinite()) {
|
||||
$lastOccurence = $maxDate->getTimeStamp();
|
||||
} else {
|
||||
$end = $it->getDtEnd();
|
||||
while($it->valid() && $end < $maxDate) {
|
||||
$end = $it->getDtEnd();
|
||||
$it->next();
|
||||
|
||||
}
|
||||
$lastOccurence = $end->getTimeStamp();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'etag' => md5($calendarData),
|
||||
'size' => strlen($calendarData),
|
||||
'componentType' => $componentType,
|
||||
'firstOccurence' => $firstOccurence,
|
||||
'lastOccurence' => $lastOccurence,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendarObject($calendarId,$objectUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
|
||||
$stmt->execute(array($calendarId,$objectUri));
|
||||
$stmt = $this->pdo->prepare('UPDATE '. $this->calendarTableName .' SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt->execute(array($calendarId));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a calendar-query on the contents of this calendar.
|
||||
*
|
||||
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||
* calendar-query it is possible for a client to request a specific set of
|
||||
* object, based on contents of iCalendar properties, date-ranges and
|
||||
* iCalendar component types (VTODO, VEVENT).
|
||||
*
|
||||
* This method should just return a list of (relative) urls that match this
|
||||
* query.
|
||||
*
|
||||
* The list of filters are specified as an array. The exact array is
|
||||
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||
*
|
||||
* Note that it is extremely likely that getCalendarObject for every path
|
||||
* returned from this method will be called almost immediately after. You
|
||||
* may want to anticipate this to speed up these requests.
|
||||
*
|
||||
* This method provides a default implementation, which parses *all* the
|
||||
* iCalendar objects in the specified calendar.
|
||||
*
|
||||
* This default may well be good enough for personal use, and calendars
|
||||
* that aren't very large. But if you anticipate high usage, big calendars
|
||||
* or high loads, you are strongly adviced to optimize certain paths.
|
||||
*
|
||||
* The best way to do so is override this method and to optimize
|
||||
* specifically for 'common filters'.
|
||||
*
|
||||
* Requests that are extremely common are:
|
||||
* * requests for just VEVENTS
|
||||
* * requests for just VTODO
|
||||
* * requests with a time-range-filter on a VEVENT.
|
||||
*
|
||||
* ..and combinations of these requests. It may not be worth it to try to
|
||||
* handle every possible situation and just rely on the (relatively
|
||||
* easy to use) CalendarQueryValidator to handle the rest.
|
||||
*
|
||||
* Note that especially time-range-filters may be difficult to parse. A
|
||||
* time-range filter specified on a VEVENT must for instance also handle
|
||||
* recurrence rules correctly.
|
||||
* A good example of how to interprete all these filters can also simply
|
||||
* be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct
|
||||
* as possible, so it gives you a good idea on what type of stuff you need
|
||||
* to think of.
|
||||
*
|
||||
* This specific implementation (for the PDO) backend optimizes filters on
|
||||
* specific components, and VEVENT time-ranges.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param array $filters
|
||||
* @return array
|
||||
*/
|
||||
public function calendarQuery($calendarId, array $filters) {
|
||||
|
||||
$result = array();
|
||||
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
||||
|
||||
$componentType = null;
|
||||
$requirePostFilter = true;
|
||||
$timeRange = null;
|
||||
|
||||
// if no filters were specified, we don't need to filter after a query
|
||||
if (!$filters['prop-filters'] && !$filters['comp-filters']) {
|
||||
$requirePostFilter = false;
|
||||
}
|
||||
|
||||
// Figuring out if there's a component filter
|
||||
if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
|
||||
$componentType = $filters['comp-filters'][0]['name'];
|
||||
|
||||
// Checking if we need post-filters
|
||||
if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) {
|
||||
$requirePostFilter = false;
|
||||
}
|
||||
// There was a time-range filter
|
||||
if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) {
|
||||
$timeRange = $filters['comp-filters'][0]['time-range'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($requirePostFilter) {
|
||||
$query = "SELECT uri, calendardata FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid";
|
||||
} else {
|
||||
$query = "SELECT uri FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid";
|
||||
}
|
||||
|
||||
$values = array(
|
||||
'calendarid' => $calendarId,
|
||||
);
|
||||
|
||||
if ($componentType) {
|
||||
$query.=" AND componenttype = :componenttype";
|
||||
$values['componenttype'] = $componentType;
|
||||
}
|
||||
|
||||
if ($timeRange && $timeRange['start']) {
|
||||
$query.=" AND lastoccurence > :startdate";
|
||||
$values['startdate'] = $timeRange['start']->getTimeStamp();
|
||||
}
|
||||
if ($timeRange && $timeRange['end']) {
|
||||
$query.=" AND firstoccurence < :enddate";
|
||||
$values['enddate'] = $timeRange['end']->getTimeStamp();
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$stmt->execute($values);
|
||||
|
||||
$result = array();
|
||||
while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
if ($requirePostFilter) {
|
||||
if (!$this->validateFilterForObject($row, $filters)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$result[] = $row['uri'];
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
}
|
366
dav/SabreDAV/lib/Sabre/CalDAV/Calendar.php
Normal file
366
dav/SabreDAV/lib/Sabre/CalDAV/Calendar.php
Normal file
|
@ -0,0 +1,366 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This object represents a CalDAV calendar.
|
||||
*
|
||||
* A calendar can contain multiple TODO and or Events. These are represented
|
||||
* as Sabre_CalDAV_CalendarObject objects.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
|
||||
|
||||
/**
|
||||
* This is an array with calendar information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $calendarInfo;
|
||||
|
||||
/**
|
||||
* CalDAV backend
|
||||
*
|
||||
* @var Sabre_CalDAV_Backend_Abstract
|
||||
*/
|
||||
protected $caldavBackend;
|
||||
|
||||
/**
|
||||
* Principal backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
||||
* @param array $calendarInfo
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) {
|
||||
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
$this->principalBackend = $principalBackend;
|
||||
$this->calendarInfo = $calendarInfo;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the calendar
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return $this->calendarInfo['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties such as the display name and description
|
||||
*
|
||||
* @param array $mutations
|
||||
* @return array
|
||||
*/
|
||||
public function updateProperties($mutations) {
|
||||
|
||||
return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of properties
|
||||
*
|
||||
* @param array $requestedProperties
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties($requestedProperties) {
|
||||
|
||||
$response = array();
|
||||
|
||||
foreach($requestedProperties as $prop) switch($prop) {
|
||||
|
||||
case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' :
|
||||
$response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData();
|
||||
break;
|
||||
case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' :
|
||||
$response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet();
|
||||
break;
|
||||
case '{DAV:}owner' :
|
||||
$response[$prop] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,$this->calendarInfo['principaluri']);
|
||||
break;
|
||||
default :
|
||||
if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop];
|
||||
break;
|
||||
|
||||
}
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a calendar object
|
||||
*
|
||||
* The contained calendar objects are for example Events or Todo's.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Sabre_CalDAV_ICalendarObject
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
|
||||
if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found');
|
||||
return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full list of calendar objects
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
|
||||
$children = array();
|
||||
foreach($objs as $obj) {
|
||||
$children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
|
||||
}
|
||||
return $children;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a child-node exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
|
||||
if (!$obj)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory
|
||||
*
|
||||
* We actually block this, as subdirectories are not allowed in calendars.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file
|
||||
*
|
||||
* The contents of the new file must be a valid ICalendar string.
|
||||
*
|
||||
* @param string $name
|
||||
* @param resource $calendarData
|
||||
* @return string|null
|
||||
*/
|
||||
public function createFile($name,$calendarData = null) {
|
||||
|
||||
if (is_resource($calendarData)) {
|
||||
$calendarData = stream_get_contents($calendarData);
|
||||
}
|
||||
return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the calendar.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the calendar. Note that most calendars use the
|
||||
* {DAV:}displayname to display a name to display a name.
|
||||
*
|
||||
* @param string $newName
|
||||
* @return void
|
||||
*/
|
||||
public function setName($newName) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date as a unix timestamp.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->calendarInfo['principaluri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->calendarInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy',
|
||||
'principal' => '{DAV:}authenticated',
|
||||
'protected' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of supported privileges for this node.
|
||||
*
|
||||
* The returned data structure is a list of nested privileges.
|
||||
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
|
||||
* standard structure.
|
||||
*
|
||||
* If null is returned from this method, the default privilege set is used,
|
||||
* which is fine for most common usecases.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getSupportedPrivilegeSet() {
|
||||
|
||||
$default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet();
|
||||
|
||||
// We need to inject 'read-free-busy' in the tree, aggregated under
|
||||
// {DAV:}read.
|
||||
foreach($default['aggregates'] as &$agg) {
|
||||
|
||||
if ($agg['privilege'] !== '{DAV:}read') continue;
|
||||
|
||||
$agg['aggregates'][] = array(
|
||||
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy',
|
||||
);
|
||||
|
||||
}
|
||||
return $default;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a calendar-query on the contents of this calendar.
|
||||
*
|
||||
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||
* calendar-query it is possible for a client to request a specific set of
|
||||
* object, based on contents of iCalendar properties, date-ranges and
|
||||
* iCalendar component types (VTODO, VEVENT).
|
||||
*
|
||||
* This method should just return a list of (relative) urls that match this
|
||||
* query.
|
||||
*
|
||||
* The list of filters are specified as an array. The exact array is
|
||||
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||
*
|
||||
* @param array $filters
|
||||
* @return array
|
||||
*/
|
||||
public function calendarQuery(array $filters) {
|
||||
|
||||
return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
|
||||
|
||||
}
|
||||
|
||||
}
|
273
dav/SabreDAV/lib/Sabre/CalDAV/CalendarObject.php
Normal file
273
dav/SabreDAV/lib/Sabre/CalDAV/CalendarObject.php
Normal file
|
@ -0,0 +1,273 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The CalendarObject represents a single VEVENT or VTODO within a Calendar.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV_ICalendarObject, Sabre_DAVACL_IACL {
|
||||
|
||||
/**
|
||||
* Sabre_CalDAV_Backend_Abstract
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $caldavBackend;
|
||||
|
||||
/**
|
||||
* Array with information about this CalendarObject
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $objectData;
|
||||
|
||||
/**
|
||||
* Array with information about the containing calendar
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $calendarInfo;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
||||
* @param array $calendarInfo
|
||||
* @param array $objectData
|
||||
*/
|
||||
public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) {
|
||||
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
|
||||
if (!isset($objectData['calendarid'])) {
|
||||
throw new InvalidArgumentException('The objectData argument must contain a \'calendarid\' property');
|
||||
}
|
||||
if (!isset($objectData['uri'])) {
|
||||
throw new InvalidArgumentException('The objectData argument must contain an \'uri\' property');
|
||||
}
|
||||
|
||||
$this->calendarInfo = $calendarInfo;
|
||||
$this->objectData = $objectData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uri for this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return $this->objectData['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ICalendar-formatted object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get() {
|
||||
|
||||
// Pre-populating the 'calendardata' is optional, if we don't have it
|
||||
// already we fetch it from the backend.
|
||||
if (!isset($this->objectData['calendardata'])) {
|
||||
$this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']);
|
||||
}
|
||||
return $this->objectData['calendardata'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ICalendar-formatted object
|
||||
*
|
||||
* @param string|resource $calendarData
|
||||
* @return string
|
||||
*/
|
||||
public function put($calendarData) {
|
||||
|
||||
if (is_resource($calendarData)) {
|
||||
$calendarData = stream_get_contents($calendarData);
|
||||
}
|
||||
$etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData);
|
||||
$this->objectData['calendardata'] = $calendarData;
|
||||
$this->objectData['etag'] = $etag;
|
||||
|
||||
return $etag;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the calendar object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime content-type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType() {
|
||||
|
||||
return 'text/calendar; charset=utf-8';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ETag for this object.
|
||||
*
|
||||
* The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getETag() {
|
||||
|
||||
if (isset($this->objectData['etag'])) {
|
||||
return $this->objectData['etag'];
|
||||
} else {
|
||||
return '"' . md5($this->get()). '"';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date as a unix timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return $this->objectData['lastmodified'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of this object in bytes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize() {
|
||||
|
||||
if (array_key_exists('size',$this->objectData)) {
|
||||
return $this->objectData['size'];
|
||||
} else {
|
||||
return strlen($this->get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->calendarInfo['principaluri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->calendarInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of supported privileges for this node.
|
||||
*
|
||||
* The returned data structure is a list of nested privileges.
|
||||
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
|
||||
* standard structure.
|
||||
*
|
||||
* If null is returned from this method, the default privilege set is used,
|
||||
* which is fine for most common usecases.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getSupportedPrivilegeSet() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
296
dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryParser.php
Normal file
296
dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryParser.php
Normal file
|
@ -0,0 +1,296 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Parses the calendar-query report request body.
|
||||
*
|
||||
* Whoever designed this format, and the CalDAV equivalent even more so,
|
||||
* has no feel for design.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_CalendarQueryParser {
|
||||
|
||||
/**
|
||||
* List of requested properties the client wanted
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $requestedProperties;
|
||||
|
||||
/**
|
||||
* List of property/component filters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $filters;
|
||||
|
||||
/**
|
||||
* This property will contain null if CALDAV:expand was not specified,
|
||||
* otherwise it will contain an array with 2 elements (start, end). Each
|
||||
* contain a DateTime object.
|
||||
*
|
||||
* If expand is specified, recurring calendar objects are to be expanded
|
||||
* into their individual components, and only the components that fall
|
||||
* within the specified time-range are to be returned.
|
||||
*
|
||||
* For more details, see rfc4791, section 9.6.5.
|
||||
*
|
||||
* @var null|array
|
||||
*/
|
||||
public $expand;
|
||||
|
||||
/**
|
||||
* DOM Document
|
||||
*
|
||||
* @var DOMDocument
|
||||
*/
|
||||
protected $dom;
|
||||
|
||||
/**
|
||||
* DOM XPath object
|
||||
*
|
||||
* @var DOMXPath
|
||||
*/
|
||||
protected $xpath;
|
||||
|
||||
/**
|
||||
* Creates the parser
|
||||
*
|
||||
* @param DOMDocument $dom
|
||||
*/
|
||||
public function __construct(DOMDocument $dom) {
|
||||
|
||||
$this->dom = $dom;
|
||||
|
||||
$this->xpath = new DOMXPath($dom);
|
||||
$this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
|
||||
$this->xpath->registerNameSpace('dav','urn:DAV');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function parse() {
|
||||
|
||||
$filterNode = null;
|
||||
|
||||
$filter = $this->xpath->query('/cal:calendar-query/cal:filter');
|
||||
if ($filter->length !== 1) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
|
||||
}
|
||||
|
||||
$compFilters = $this->parseCompFilters($filter->item(0));
|
||||
if (count($compFilters)!==1) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('There must be exactly 1 top-level comp-filter.');
|
||||
}
|
||||
|
||||
$this->filters = $compFilters[0];
|
||||
$this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
|
||||
|
||||
$expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand');
|
||||
if ($expand->length>0) {
|
||||
$this->expand = $this->parseExpand($expand->item(0));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses all the 'comp-filter' elements from a node
|
||||
*
|
||||
* @param DOMElement $parentNode
|
||||
* @return array
|
||||
*/
|
||||
protected function parseCompFilters(DOMElement $parentNode) {
|
||||
|
||||
$compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode);
|
||||
$result = array();
|
||||
|
||||
for($ii=0; $ii < $compFilterNodes->length; $ii++) {
|
||||
|
||||
$compFilterNode = $compFilterNodes->item($ii);
|
||||
|
||||
$compFilter = array();
|
||||
$compFilter['name'] = $compFilterNode->getAttribute('name');
|
||||
$compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0;
|
||||
$compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode);
|
||||
$compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode);
|
||||
$compFilter['time-range'] = $this->parseTimeRange($compFilterNode);
|
||||
|
||||
if ($compFilter['time-range'] && !in_array($compFilter['name'],array(
|
||||
'VEVENT',
|
||||
'VTODO',
|
||||
'VJOURNAL',
|
||||
'VFREEBUSY',
|
||||
'VALARM',
|
||||
))) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component');
|
||||
};
|
||||
|
||||
$result[] = $compFilter;
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses all the prop-filter elements from a node
|
||||
*
|
||||
* @param DOMElement $parentNode
|
||||
* @return array
|
||||
*/
|
||||
protected function parsePropFilters(DOMElement $parentNode) {
|
||||
|
||||
$propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode);
|
||||
$result = array();
|
||||
|
||||
for ($ii=0; $ii < $propFilterNodes->length; $ii++) {
|
||||
|
||||
$propFilterNode = $propFilterNodes->item($ii);
|
||||
$propFilter = array();
|
||||
$propFilter['name'] = $propFilterNode->getAttribute('name');
|
||||
$propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0;
|
||||
$propFilter['param-filters'] = $this->parseParamFilters($propFilterNode);
|
||||
$propFilter['text-match'] = $this->parseTextMatch($propFilterNode);
|
||||
$propFilter['time-range'] = $this->parseTimeRange($propFilterNode);
|
||||
|
||||
$result[] = $propFilter;
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the param-filter element
|
||||
*
|
||||
* @param DOMElement $parentNode
|
||||
* @return array
|
||||
*/
|
||||
protected function parseParamFilters(DOMElement $parentNode) {
|
||||
|
||||
$paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode);
|
||||
$result = array();
|
||||
|
||||
for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
|
||||
|
||||
$paramFilterNode = $paramFilterNodes->item($ii);
|
||||
$paramFilter = array();
|
||||
$paramFilter['name'] = $paramFilterNode->getAttribute('name');
|
||||
$paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0;
|
||||
$paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode);
|
||||
|
||||
$result[] = $paramFilter;
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the text-match element
|
||||
*
|
||||
* @param DOMElement $parentNode
|
||||
* @return array|null
|
||||
*/
|
||||
protected function parseTextMatch(DOMElement $parentNode) {
|
||||
|
||||
$textMatchNodes = $this->xpath->query('cal:text-match', $parentNode);
|
||||
|
||||
if ($textMatchNodes->length === 0)
|
||||
return null;
|
||||
|
||||
$textMatchNode = $textMatchNodes->item(0);
|
||||
$negateCondition = $textMatchNode->getAttribute('negate-condition');
|
||||
$negateCondition = $negateCondition==='yes';
|
||||
$collation = $textMatchNode->getAttribute('collation');
|
||||
if (!$collation) $collation = 'i;ascii-casemap';
|
||||
|
||||
return array(
|
||||
'negate-condition' => $negateCondition,
|
||||
'collation' => $collation,
|
||||
'value' => $textMatchNode->nodeValue
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the time-range element
|
||||
*
|
||||
* @param DOMElement $parentNode
|
||||
* @return array|null
|
||||
*/
|
||||
protected function parseTimeRange(DOMElement $parentNode) {
|
||||
|
||||
$timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode);
|
||||
if ($timeRangeNodes->length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$timeRangeNode = $timeRangeNodes->item(0);
|
||||
|
||||
if ($start = $timeRangeNode->getAttribute('start')) {
|
||||
$start = Sabre_VObject_DateTimeParser::parseDateTime($start);
|
||||
} else {
|
||||
$start = null;
|
||||
}
|
||||
if ($end = $timeRangeNode->getAttribute('end')) {
|
||||
$end = Sabre_VObject_DateTimeParser::parseDateTime($end);
|
||||
} else {
|
||||
$end = null;
|
||||
}
|
||||
|
||||
if (!is_null($start) && !is_null($end) && $end <= $start) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter');
|
||||
}
|
||||
|
||||
return array(
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the CALDAV:expand element
|
||||
*
|
||||
* @param DOMElement $parentNode
|
||||
* @return void
|
||||
*/
|
||||
protected function parseExpand(DOMElement $parentNode) {
|
||||
|
||||
$start = $parentNode->getAttribute('start');
|
||||
if(!$start) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element');
|
||||
}
|
||||
$start = Sabre_VObject_DateTimeParser::parseDateTime($start);
|
||||
|
||||
$end = $parentNode->getAttribute('end');
|
||||
if(!$end) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element');
|
||||
}
|
||||
$end = Sabre_VObject_DateTimeParser::parseDateTime($end);
|
||||
|
||||
if ($end <= $start) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
|
||||
}
|
||||
|
||||
return array(
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
369
dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryValidator.php
Normal file
369
dav/SabreDAV/lib/Sabre/CalDAV/CalendarQueryValidator.php
Normal file
|
@ -0,0 +1,369 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CalendarQuery Validator
|
||||
*
|
||||
* This class is responsible for checking if an iCalendar object matches a set
|
||||
* of filters. The main function to do this is 'validate'.
|
||||
*
|
||||
* This is used to determine which icalendar objects should be returned for a
|
||||
* calendar-query REPORT request.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_CalendarQueryValidator {
|
||||
|
||||
/**
|
||||
* Verify if a list of filters applies to the calendar data object
|
||||
*
|
||||
* The list of filters must be formatted as parsed by Sabre_CalDAV_CalendarQueryParser
|
||||
*
|
||||
* @param Sabre_VObject_Component $vObject
|
||||
* @param array $filters
|
||||
* @return bool
|
||||
*/
|
||||
public function validate(Sabre_VObject_Component $vObject,array $filters) {
|
||||
|
||||
// The top level object is always a component filter.
|
||||
// We'll parse it manually, as it's pretty simple.
|
||||
if ($vObject->name !== $filters['name']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return
|
||||
$this->validateCompFilters($vObject, $filters['comp-filters']) &&
|
||||
$this->validatePropFilters($vObject, $filters['prop-filters']);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks the validity of comp-filters.
|
||||
*
|
||||
* A list of comp-filters needs to be specified. Also the parent of the
|
||||
* component we're checking should be specified, not the component to check
|
||||
* itself.
|
||||
*
|
||||
* @param Sabre_VObject_Component $parent
|
||||
* @param array $filters
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateCompFilters(Sabre_VObject_Component $parent, array $filters) {
|
||||
|
||||
foreach($filters as $filter) {
|
||||
|
||||
$isDefined = isset($parent->$filter['name']);
|
||||
|
||||
if ($filter['is-not-defined']) {
|
||||
|
||||
if ($isDefined) {
|
||||
return false;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
if (!$isDefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($filter['time-range']) {
|
||||
foreach($parent->$filter['name'] as $subComponent) {
|
||||
if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$filter['comp-filters'] && !$filter['prop-filters']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there are sub-filters, we need to find at least one component
|
||||
// for which the subfilters hold true.
|
||||
foreach($parent->$filter['name'] as $subComponent) {
|
||||
|
||||
if (
|
||||
$this->validateCompFilters($subComponent, $filter['comp-filters']) &&
|
||||
$this->validatePropFilters($subComponent, $filter['prop-filters'])) {
|
||||
// We had a match, so this comp-filter succeeds
|
||||
continue 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If we got here it means there were sub-comp-filters or
|
||||
// sub-prop-filters and there was no match. This means this filter
|
||||
// needs to return false.
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// If we got here it means we got through all comp-filters alive so the
|
||||
// filters were all true.
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks the validity of prop-filters.
|
||||
*
|
||||
* A list of prop-filters needs to be specified. Also the parent of the
|
||||
* property we're checking should be specified, not the property to check
|
||||
* itself.
|
||||
*
|
||||
* @param Sabre_VObject_Component $parent
|
||||
* @param array $filters
|
||||
* @return bool
|
||||
*/
|
||||
protected function validatePropFilters(Sabre_VObject_Component $parent, array $filters) {
|
||||
|
||||
foreach($filters as $filter) {
|
||||
|
||||
$isDefined = isset($parent->$filter['name']);
|
||||
|
||||
if ($filter['is-not-defined']) {
|
||||
|
||||
if ($isDefined) {
|
||||
return false;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
if (!$isDefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($filter['time-range']) {
|
||||
foreach($parent->$filter['name'] as $subComponent) {
|
||||
if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$filter['param-filters'] && !$filter['text-match']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there are sub-filters, we need to find at least one property
|
||||
// for which the subfilters hold true.
|
||||
foreach($parent->$filter['name'] as $subComponent) {
|
||||
|
||||
if(
|
||||
$this->validateParamFilters($subComponent, $filter['param-filters']) &&
|
||||
(!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match']))
|
||||
) {
|
||||
// We had a match, so this prop-filter succeeds
|
||||
continue 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If we got here it means there were sub-param-filters or
|
||||
// text-match filters and there was no match. This means the
|
||||
// filter needs to return false.
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// If we got here it means we got through all prop-filters alive so the
|
||||
// filters were all true.
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks the validity of param-filters.
|
||||
*
|
||||
* A list of param-filters needs to be specified. Also the parent of the
|
||||
* parameter we're checking should be specified, not the parameter to check
|
||||
* itself.
|
||||
*
|
||||
* @param Sabre_VObject_Property $parent
|
||||
* @param array $filters
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateParamFilters(Sabre_VObject_Property $parent, array $filters) {
|
||||
|
||||
foreach($filters as $filter) {
|
||||
|
||||
$isDefined = isset($parent[$filter['name']]);
|
||||
|
||||
if ($filter['is-not-defined']) {
|
||||
|
||||
if ($isDefined) {
|
||||
return false;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
if (!$isDefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$filter['text-match']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there are sub-filters, we need to find at least one parameter
|
||||
// for which the subfilters hold true.
|
||||
foreach($parent[$filter['name']] as $subParam) {
|
||||
|
||||
if($this->validateTextMatch($subParam,$filter['text-match'])) {
|
||||
// We had a match, so this param-filter succeeds
|
||||
continue 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If we got here it means there was a text-match filter and there
|
||||
// were no matches. This means the filter needs to return false.
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// If we got here it means we got through all param-filters alive so the
|
||||
// filters were all true.
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks the validity of a text-match.
|
||||
*
|
||||
* A single text-match should be specified as well as the specific property
|
||||
* or parameter we need to validate.
|
||||
*
|
||||
* @param Sabre_VObject_Node $parent
|
||||
* @param array $textMatch
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) {
|
||||
|
||||
$value = (string)$parent;
|
||||
|
||||
$isMatching = Sabre_DAV_StringUtil::textMatch($value, $textMatch['value'], $textMatch['collation']);
|
||||
|
||||
return ($textMatch['negate-condition'] xor $isMatching);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a component matches the given time range.
|
||||
*
|
||||
* This is all based on the rules specified in rfc4791, which are quite
|
||||
* complex.
|
||||
*
|
||||
* @param Sabre_VObject_Node $component
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) {
|
||||
|
||||
if (is_null($start)) {
|
||||
$start = new DateTime('1900-01-01');
|
||||
}
|
||||
if (is_null($end)) {
|
||||
$end = new DateTime('3000-01-01');
|
||||
}
|
||||
|
||||
switch($component->name) {
|
||||
|
||||
case 'VEVENT' :
|
||||
case 'VTODO' :
|
||||
case 'VJOURNAL' :
|
||||
|
||||
return $component->isInTimeRange($start, $end);
|
||||
|
||||
case 'VALARM' :
|
||||
|
||||
// If the valarm is wrapped in a recurring event, we need to
|
||||
// expand the recursions, and validate each.
|
||||
//
|
||||
// Our datamodel doesn't easily allow us to do this straight
|
||||
// in the VALARM component code, so this is a hack, and an
|
||||
// expensive one too.
|
||||
if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
|
||||
|
||||
// Fire up the iterator!
|
||||
$it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
|
||||
while($it->valid()) {
|
||||
$expandedEvent = $it->getEventObject();
|
||||
|
||||
// We need to check from these expanded alarms, which
|
||||
// one is the first to trigger. Based on this, we can
|
||||
// determine if we can 'give up' expanding events.
|
||||
$firstAlarm = null;
|
||||
foreach($expandedEvent->VALARM as $expandedAlarm) {
|
||||
|
||||
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
|
||||
if ($expandedAlarm->isInTimeRange($start, $end)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') {
|
||||
// This is an alarm with a non-relative trigger
|
||||
// time, likely created by a buggy client. The
|
||||
// implication is that every alarm in this
|
||||
// recurring event trigger at the exact same
|
||||
// time. It doesn't make sense to traverse
|
||||
// further.
|
||||
} else {
|
||||
// We store the first alarm as a means to
|
||||
// figure out when we can stop traversing.
|
||||
if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
|
||||
$firstAlarm = $effectiveTrigger;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (is_null($firstAlarm)) {
|
||||
// No alarm was found.
|
||||
//
|
||||
// Or technically: No alarm that will change for
|
||||
// every instance of the recurrence was found,
|
||||
// which means we can assume there was no match.
|
||||
return false;
|
||||
}
|
||||
if ($firstAlarm > $end) {
|
||||
return false;
|
||||
}
|
||||
$it->next();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return $component->isInTimeRange($start, $end);
|
||||
}
|
||||
|
||||
case 'VFREEBUSY' :
|
||||
throw new Sabre_DAV_Exception_NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components');
|
||||
|
||||
case 'COMPLETED' :
|
||||
case 'CREATED' :
|
||||
case 'DTEND' :
|
||||
case 'DTSTAMP' :
|
||||
case 'DTSTART' :
|
||||
case 'DUE' :
|
||||
case 'LAST-MODIFIED' :
|
||||
return ($start <= $component->getDateTime() && $end >= $component->getDateTime());
|
||||
|
||||
|
||||
|
||||
default :
|
||||
throw new Sabre_DAV_Exception_BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
76
dav/SabreDAV/lib/Sabre/CalDAV/CalendarRootNode.php
Normal file
76
dav/SabreDAV/lib/Sabre/CalDAV/CalendarRootNode.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Calendars collection
|
||||
*
|
||||
* This object is responsible for generating a list of calendar-homes for each
|
||||
* user.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollection {
|
||||
|
||||
/**
|
||||
* CalDAV backend
|
||||
*
|
||||
* @var Sabre_CalDAV_Backend_Abstract
|
||||
*/
|
||||
protected $caldavBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* This constructor needs both an authentication and a caldav backend.
|
||||
*
|
||||
* By default this class will show a list of calendar collections for
|
||||
* principals in the 'principals' collection. If your main principals are
|
||||
* actually located in a different path, use the $principalPrefix argument
|
||||
* to override this.
|
||||
*
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
||||
* @param string $principalPrefix
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') {
|
||||
|
||||
parent::__construct($principalBackend, $principalPrefix);
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nodename
|
||||
*
|
||||
* We're overriding this, because the default will be the 'principalPrefix',
|
||||
* and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return Sabre_CalDAV_Plugin::CALENDAR_ROOT;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a node for a principal.
|
||||
*
|
||||
* The passed array contains principal information, and is guaranteed to
|
||||
* at least contain a uri item. Other properties may or may not be
|
||||
* supplied by the authentication backend.
|
||||
*
|
||||
* @param array $principal
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChildForPrincipal(array $principal) {
|
||||
|
||||
return new Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']);
|
||||
|
||||
}
|
||||
|
||||
}
|
139
dav/SabreDAV/lib/Sabre/CalDAV/ICSExportPlugin.php
Normal file
139
dav/SabreDAV/lib/Sabre/CalDAV/ICSExportPlugin.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ICS Exporter
|
||||
*
|
||||
* This plugin adds the ability to export entire calendars as .ics files.
|
||||
* This is useful for clients that don't support CalDAV yet. They often do
|
||||
* support ics files.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* Reference to Server class
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* Initializes the plugin and registers event handlers
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
$this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 'beforeMethod' event handles. This event handles intercepts GET requests ending
|
||||
* with ?export
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $uri
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeMethod($method, $uri) {
|
||||
|
||||
if ($method!='GET') return;
|
||||
if ($this->server->httpRequest->getQueryString()!='export') return;
|
||||
|
||||
// splitting uri
|
||||
list($uri) = explode('?',$uri,2);
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
|
||||
if (!($node instanceof Sabre_CalDAV_Calendar)) return;
|
||||
|
||||
// Checking ACL, if available.
|
||||
if ($aclPlugin = $this->server->getPlugin('acl')) {
|
||||
$aclPlugin->checkPrivileges($uri, '{DAV:}read');
|
||||
}
|
||||
|
||||
$this->server->httpResponse->setHeader('Content-Type','text/calendar');
|
||||
$this->server->httpResponse->sendStatus(200);
|
||||
|
||||
$nodes = $this->server->getPropertiesForPath($uri, array(
|
||||
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data',
|
||||
),1);
|
||||
|
||||
$this->server->httpResponse->sendBody($this->generateICS($nodes));
|
||||
|
||||
// Returning false to break the event chain
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all calendar objects, and builds one big ics export
|
||||
*
|
||||
* @param array $nodes
|
||||
* @return string
|
||||
*/
|
||||
public function generateICS(array $nodes) {
|
||||
|
||||
$calendar = new Sabre_VObject_Component('vcalendar');
|
||||
$calendar->version = '2.0';
|
||||
if (Sabre_DAV_Server::$exposeVersion) {
|
||||
$calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
|
||||
} else {
|
||||
$calendar->prodid = '-//SabreDAV//SabreDAV//EN';
|
||||
}
|
||||
$calendar->calscale = 'GREGORIAN';
|
||||
|
||||
$collectedTimezones = array();
|
||||
|
||||
$timezones = array();
|
||||
$objects = array();
|
||||
|
||||
foreach($nodes as $node) {
|
||||
|
||||
if (!isset($node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'])) {
|
||||
continue;
|
||||
}
|
||||
$nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'];
|
||||
|
||||
$nodeComp = Sabre_VObject_Reader::read($nodeData);
|
||||
|
||||
foreach($nodeComp->children() as $child) {
|
||||
|
||||
switch($child->name) {
|
||||
case 'VEVENT' :
|
||||
case 'VTODO' :
|
||||
case 'VJOURNAL' :
|
||||
$objects[] = $child;
|
||||
break;
|
||||
|
||||
// VTIMEZONE is special, because we need to filter out the duplicates
|
||||
case 'VTIMEZONE' :
|
||||
// Naively just checking tzid.
|
||||
if (in_array((string)$child->TZID, $collectedTimezones)) continue;
|
||||
|
||||
$timezones[] = $child;
|
||||
$collectedTimezones[] = $child->TZID;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach($timezones as $tz) $calendar->add($tz);
|
||||
foreach($objects as $obj) $calendar->add($obj);
|
||||
|
||||
return $calendar->serialize();
|
||||
|
||||
}
|
||||
|
||||
}
|
35
dav/SabreDAV/lib/Sabre/CalDAV/ICalendar.php
Normal file
35
dav/SabreDAV/lib/Sabre/CalDAV/ICalendar.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Calendar interface
|
||||
*
|
||||
* Implement this interface to allow a node to be recognized as an calendar.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_CalDAV_ICalendar extends Sabre_DAV_ICollection {
|
||||
|
||||
/**
|
||||
* Performs a calendar-query on the contents of this calendar.
|
||||
*
|
||||
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||
* calendar-query it is possible for a client to request a specific set of
|
||||
* object, based on contents of iCalendar properties, date-ranges and
|
||||
* iCalendar component types (VTODO, VEVENT).
|
||||
*
|
||||
* This method should just return a list of (relative) urls that match this
|
||||
* query.
|
||||
*
|
||||
* The list of filters are specified as an array. The exact array is
|
||||
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||
*
|
||||
* @param array $filters
|
||||
* @return array
|
||||
*/
|
||||
public function calendarQuery(array $filters);
|
||||
|
||||
}
|
20
dav/SabreDAV/lib/Sabre/CalDAV/ICalendarObject.php
Normal file
20
dav/SabreDAV/lib/Sabre/CalDAV/ICalendarObject.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CalendarObject interface
|
||||
/**
|
||||
* Extend the ICalendarObject interface to allow your custom nodes to be picked up as
|
||||
* CalendarObjects.
|
||||
*
|
||||
* Calendar objects are resources such as Events, Todo's or Journals.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_CalDAV_ICalendarObject extends Sabre_DAV_IFile {
|
||||
|
||||
}
|
||||
|
894
dav/SabreDAV/lib/Sabre/CalDAV/Plugin.php
Normal file
894
dav/SabreDAV/lib/Sabre/CalDAV/Plugin.php
Normal file
|
@ -0,0 +1,894 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CalDAV plugin
|
||||
*
|
||||
* This plugin provides functionality added by CalDAV (RFC 4791)
|
||||
* It implements new reports, and the MKCALENDAR method.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||
|
||||
/**
|
||||
* This is the official CalDAV namespace
|
||||
*/
|
||||
const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav';
|
||||
|
||||
/**
|
||||
* This is the namespace for the proprietary calendarserver extensions
|
||||
*/
|
||||
const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
|
||||
|
||||
/**
|
||||
* The hardcoded root for calendar objects. It is unfortunate
|
||||
* that we're stuck with it, but it will have to do for now
|
||||
*/
|
||||
const CALENDAR_ROOT = 'calendars';
|
||||
|
||||
/**
|
||||
* Reference to server object
|
||||
*
|
||||
* @var Sabre_DAV_Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* The email handler for invites and other scheduling messages.
|
||||
*
|
||||
* @var Sabre_CalDAV_Schedule_IMip
|
||||
*/
|
||||
protected $imipHandler;
|
||||
|
||||
/**
|
||||
* Sets the iMIP handler.
|
||||
*
|
||||
* iMIP = The email transport of iCalendar scheduling messages. Setting
|
||||
* this is optional, but if you want the server to allow invites to be sent
|
||||
* out, you must set a handler.
|
||||
*
|
||||
* Specifically iCal will plain assume that the server supports this. If
|
||||
* the server doesn't, iCal will display errors when inviting people to
|
||||
* events.
|
||||
*
|
||||
* @param Sabre_CalDAV_Schedule_IMip $imipHandler
|
||||
* @return void
|
||||
*/
|
||||
public function setIMipHandler(Sabre_CalDAV_Schedule_IMip $imipHandler) {
|
||||
|
||||
$this->imipHandler = $imipHandler;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to tell the server this plugin defines additional
|
||||
* HTTP methods.
|
||||
*
|
||||
* This method is passed a uri. It should only return HTTP methods that are
|
||||
* available for the specified uri.
|
||||
*
|
||||
* @param string $uri
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPMethods($uri) {
|
||||
|
||||
// The MKCALENDAR is only available on unmapped uri's, whose
|
||||
// parents extend IExtendedCollection
|
||||
list($parent, $name) = Sabre_DAV_URLUtil::splitPath($uri);
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($parent);
|
||||
|
||||
if ($node instanceof Sabre_DAV_IExtendedCollection) {
|
||||
try {
|
||||
$node->getChild($name);
|
||||
} catch (Sabre_DAV_Exception_NotFound $e) {
|
||||
return array('MKCALENDAR');
|
||||
}
|
||||
}
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of features for the DAV: HTTP header.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeatures() {
|
||||
|
||||
return array('calendar-access', 'calendar-proxy');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using Sabre_DAV_Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName() {
|
||||
|
||||
return 'caldav';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of reports this plugin supports.
|
||||
*
|
||||
* This will be used in the {DAV:}supported-report-set property.
|
||||
* Note that you still need to subscribe to the 'report' event to actually
|
||||
* implement them
|
||||
*
|
||||
* @param string $uri
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedReportSet($uri) {
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
|
||||
$reports = array();
|
||||
if ($node instanceof Sabre_CalDAV_ICalendar || $node instanceof Sabre_CalDAV_ICalendarObject) {
|
||||
$reports[] = '{' . self::NS_CALDAV . '}calendar-multiget';
|
||||
$reports[] = '{' . self::NS_CALDAV . '}calendar-query';
|
||||
}
|
||||
if ($node instanceof Sabre_CalDAV_ICalendar) {
|
||||
$reports[] = '{' . self::NS_CALDAV . '}free-busy-query';
|
||||
}
|
||||
return $reports;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(Sabre_DAV_Server $server) {
|
||||
|
||||
$this->server = $server;
|
||||
|
||||
$server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
|
||||
//$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000);
|
||||
$server->subscribeEvent('report',array($this,'report'));
|
||||
$server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
|
||||
$server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
|
||||
$server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
|
||||
$server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
|
||||
$server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
|
||||
|
||||
$server->xmlNamespaces[self::NS_CALDAV] = 'cal';
|
||||
$server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
|
||||
|
||||
$server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet';
|
||||
|
||||
$server->resourceTypeMapping['Sabre_CalDAV_ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
|
||||
$server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox';
|
||||
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
|
||||
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
|
||||
|
||||
array_push($server->protectedProperties,
|
||||
|
||||
'{' . self::NS_CALDAV . '}supported-calendar-component-set',
|
||||
'{' . self::NS_CALDAV . '}supported-calendar-data',
|
||||
'{' . self::NS_CALDAV . '}max-resource-size',
|
||||
'{' . self::NS_CALDAV . '}min-date-time',
|
||||
'{' . self::NS_CALDAV . '}max-date-time',
|
||||
'{' . self::NS_CALDAV . '}max-instances',
|
||||
'{' . self::NS_CALDAV . '}max-attendees-per-instance',
|
||||
'{' . self::NS_CALDAV . '}calendar-home-set',
|
||||
'{' . self::NS_CALDAV . '}supported-collation-set',
|
||||
'{' . self::NS_CALDAV . '}calendar-data',
|
||||
|
||||
// scheduling extension
|
||||
'{' . self::NS_CALDAV . '}schedule-inbox-URL',
|
||||
'{' . self::NS_CALDAV . '}schedule-outbox-URL',
|
||||
'{' . self::NS_CALDAV . '}calendar-user-address-set',
|
||||
'{' . self::NS_CALDAV . '}calendar-user-type',
|
||||
|
||||
// CalendarServer extensions
|
||||
'{' . self::NS_CALENDARSERVER . '}getctag',
|
||||
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
|
||||
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles support for the MKCALENDAR method
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $uri
|
||||
* @return bool
|
||||
*/
|
||||
public function unknownMethod($method, $uri) {
|
||||
|
||||
switch ($method) {
|
||||
case 'MKCALENDAR' :
|
||||
$this->httpMkCalendar($uri);
|
||||
// false is returned to stop the propagation of the
|
||||
// unknownMethod event.
|
||||
return false;
|
||||
case 'POST' :
|
||||
// Checking if we're talking to an outbox
|
||||
try {
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
} catch (Sabre_DAV_Exception_NotFound $e) {
|
||||
return;
|
||||
}
|
||||
if (!$node instanceof Sabre_CalDAV_Schedule_IOutbox)
|
||||
return;
|
||||
|
||||
$this->outboxRequest($node);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions handles REPORT requests specific to CalDAV
|
||||
*
|
||||
* @param string $reportName
|
||||
* @param DOMNode $dom
|
||||
* @return bool
|
||||
*/
|
||||
public function report($reportName,$dom) {
|
||||
|
||||
switch($reportName) {
|
||||
case '{'.self::NS_CALDAV.'}calendar-multiget' :
|
||||
$this->calendarMultiGetReport($dom);
|
||||
return false;
|
||||
case '{'.self::NS_CALDAV.'}calendar-query' :
|
||||
$this->calendarQueryReport($dom);
|
||||
return false;
|
||||
case '{'.self::NS_CALDAV.'}free-busy-query' :
|
||||
$this->freeBusyQueryReport($dom);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the MKCALENDAR HTTP method, which creates
|
||||
* a new calendar.
|
||||
*
|
||||
* @param string $uri
|
||||
* @return void
|
||||
*/
|
||||
public function httpMkCalendar($uri) {
|
||||
|
||||
// Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support
|
||||
// for clients matching iCal in the user agent
|
||||
//$ua = $this->server->httpRequest->getHeader('User-Agent');
|
||||
//if (strpos($ua,'iCal/')!==false) {
|
||||
// throw new Sabre_DAV_Exception_Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.');
|
||||
//}
|
||||
|
||||
$body = $this->server->httpRequest->getBody(true);
|
||||
$properties = array();
|
||||
|
||||
if ($body) {
|
||||
|
||||
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
|
||||
|
||||
foreach($dom->firstChild->childNodes as $child) {
|
||||
|
||||
if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue;
|
||||
foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) {
|
||||
$properties[$k] = $prop;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
|
||||
|
||||
$this->server->createCollection($uri,$resourceType,$properties);
|
||||
|
||||
$this->server->httpResponse->sendStatus(201);
|
||||
$this->server->httpResponse->setHeader('Content-Length',0);
|
||||
}
|
||||
|
||||
/**
|
||||
* beforeGetProperties
|
||||
*
|
||||
* This method handler is invoked before any after properties for a
|
||||
* resource are fetched. This allows us to add in any CalDAV specific
|
||||
* properties.
|
||||
*
|
||||
* @param string $path
|
||||
* @param Sabre_DAV_INode $node
|
||||
* @param array $requestedProperties
|
||||
* @param array $returnedProperties
|
||||
* @return void
|
||||
*/
|
||||
public function beforeGetProperties($path, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
|
||||
|
||||
if ($node instanceof Sabre_DAVACL_IPrincipal) {
|
||||
|
||||
// calendar-home-set property
|
||||
$calHome = '{' . self::NS_CALDAV . '}calendar-home-set';
|
||||
if (in_array($calHome,$requestedProperties)) {
|
||||
$principalId = $node->getName();
|
||||
$calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/';
|
||||
unset($requestedProperties[$calHome]);
|
||||
$returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath);
|
||||
}
|
||||
|
||||
// schedule-outbox-URL property
|
||||
$scheduleProp = '{' . self::NS_CALDAV . '}schedule-outbox-URL';
|
||||
if (in_array($scheduleProp,$requestedProperties)) {
|
||||
$principalId = $node->getName();
|
||||
$outboxPath = self::CALENDAR_ROOT . '/' . $principalId . '/outbox';
|
||||
unset($requestedProperties[$scheduleProp]);
|
||||
$returnedProperties[200][$scheduleProp] = new Sabre_DAV_Property_Href($outboxPath);
|
||||
}
|
||||
|
||||
// calendar-user-address-set property
|
||||
$calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set';
|
||||
if (in_array($calProp,$requestedProperties)) {
|
||||
|
||||
$addresses = $node->getAlternateUriSet();
|
||||
$addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl();
|
||||
unset($requestedProperties[$calProp]);
|
||||
$returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false);
|
||||
|
||||
}
|
||||
|
||||
// These two properties are shortcuts for ical to easily find
|
||||
// other principals this principal has access to.
|
||||
$propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for';
|
||||
$propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for';
|
||||
if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) {
|
||||
|
||||
$membership = $node->getGroupMembership();
|
||||
$readList = array();
|
||||
$writeList = array();
|
||||
|
||||
foreach($membership as $group) {
|
||||
|
||||
$groupNode = $this->server->tree->getNodeForPath($group);
|
||||
|
||||
// If the node is either ap proxy-read or proxy-write
|
||||
// group, we grab the parent principal and add it to the
|
||||
// list.
|
||||
if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) {
|
||||
list($readList[]) = Sabre_DAV_URLUtil::splitPath($group);
|
||||
}
|
||||
if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyWrite) {
|
||||
list($writeList[]) = Sabre_DAV_URLUtil::splitPath($group);
|
||||
}
|
||||
|
||||
}
|
||||
if (in_array($propRead,$requestedProperties)) {
|
||||
unset($requestedProperties[$propRead]);
|
||||
$returnedProperties[200][$propRead] = new Sabre_DAV_Property_HrefList($readList);
|
||||
}
|
||||
if (in_array($propWrite,$requestedProperties)) {
|
||||
unset($requestedProperties[$propWrite]);
|
||||
$returnedProperties[200][$propWrite] = new Sabre_DAV_Property_HrefList($writeList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // instanceof IPrincipal
|
||||
|
||||
|
||||
if ($node instanceof Sabre_CalDAV_ICalendarObject) {
|
||||
// The calendar-data property is not supposed to be a 'real'
|
||||
// property, but in large chunks of the spec it does act as such.
|
||||
// Therefore we simply expose it as a property.
|
||||
$calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data';
|
||||
if (in_array($calDataProp, $requestedProperties)) {
|
||||
unset($requestedProperties[$calDataProp]);
|
||||
$val = $node->get();
|
||||
if (is_resource($val))
|
||||
$val = stream_get_contents($val);
|
||||
|
||||
// Taking out \r to not screw up the xml output
|
||||
$returnedProperties[200][$calDataProp] = str_replace("\r","", $val);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the calendar-multiget REPORT.
|
||||
*
|
||||
* This report is used by the client to fetch the content of a series
|
||||
* of urls. Effectively avoiding a lot of redundant requests.
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
public function calendarMultiGetReport($dom) {
|
||||
|
||||
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
|
||||
$hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
|
||||
|
||||
$xpath = new DOMXPath($dom);
|
||||
$xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
|
||||
$xpath->registerNameSpace('dav','urn:DAV');
|
||||
|
||||
$expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand');
|
||||
if ($expand->length>0) {
|
||||
$expandElem = $expand->item(0);
|
||||
$start = $expandElem->getAttribute('start');
|
||||
$end = $expandElem->getAttribute('end');
|
||||
if(!$start || !$end) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element');
|
||||
}
|
||||
$start = Sabre_VObject_DateTimeParser::parseDateTime($start);
|
||||
$end = Sabre_VObject_DateTimeParser::parseDateTime($end);
|
||||
|
||||
if ($end <= $start) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
|
||||
}
|
||||
|
||||
$expand = true;
|
||||
|
||||
} else {
|
||||
|
||||
$expand = false;
|
||||
|
||||
}
|
||||
|
||||
foreach($hrefElems as $elem) {
|
||||
$uri = $this->server->calculateUri($elem->nodeValue);
|
||||
list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
|
||||
|
||||
if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
|
||||
$vObject = Sabre_VObject_Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);
|
||||
$vObject->expand($start, $end);
|
||||
$objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
|
||||
}
|
||||
|
||||
$propertyList[]=$objProps;
|
||||
|
||||
}
|
||||
|
||||
$this->server->httpResponse->sendStatus(207);
|
||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles the calendar-query REPORT
|
||||
*
|
||||
* This report is used by clients to request calendar objects based on
|
||||
* complex conditions.
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
public function calendarQueryReport($dom) {
|
||||
|
||||
$parser = new Sabre_CalDAV_CalendarQueryParser($dom);
|
||||
$parser->parse();
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($this->server->getRequestUri());
|
||||
$depth = $this->server->getHTTPDepth(0);
|
||||
|
||||
// The default result is an empty array
|
||||
$result = array();
|
||||
|
||||
// The calendarobject was requested directly. In this case we handle
|
||||
// this locally.
|
||||
if ($depth == 0 && $node instanceof Sabre_CalDAV_ICalendarObject) {
|
||||
|
||||
$requestedCalendarData = true;
|
||||
$requestedProperties = $parser->requestedProperties;
|
||||
|
||||
if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) {
|
||||
|
||||
// We always retrieve calendar-data, as we need it for filtering.
|
||||
$requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data';
|
||||
|
||||
// If calendar-data wasn't explicitly requested, we need to remove
|
||||
// it after processing.
|
||||
$requestedCalendarData = false;
|
||||
}
|
||||
|
||||
$properties = $this->server->getPropertiesForPath(
|
||||
$this->server->getRequestUri(),
|
||||
$requestedProperties,
|
||||
0
|
||||
);
|
||||
|
||||
// This array should have only 1 element, the first calendar
|
||||
// object.
|
||||
$properties = current($properties);
|
||||
|
||||
// If there wasn't any calendar-data returned somehow, we ignore
|
||||
// this.
|
||||
if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) {
|
||||
|
||||
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
||||
$vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
||||
if ($validator->validate($vObject,$parser->filters)) {
|
||||
|
||||
// If the client didn't require the calendar-data property,
|
||||
// we won't give it back.
|
||||
if (!$requestedCalendarData) {
|
||||
unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
||||
} else {
|
||||
if ($parser->expand) {
|
||||
$vObject->expand($parser->expand['start'], $parser->expand['end']);
|
||||
$properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
|
||||
}
|
||||
}
|
||||
|
||||
$result = array($properties);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// If we're dealing with a calendar, the calendar itself is responsible
|
||||
// for the calendar-query.
|
||||
if ($node instanceof Sabre_CalDAV_ICalendar && $depth = 1) {
|
||||
|
||||
$nodePaths = $node->calendarQuery($parser->filters);
|
||||
|
||||
foreach($nodePaths as $path) {
|
||||
|
||||
list($properties) =
|
||||
$this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties);
|
||||
|
||||
if ($parser->expand) {
|
||||
// We need to do some post-processing
|
||||
$vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
||||
$vObject->expand($parser->expand['start'], $parser->expand['end']);
|
||||
$properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
|
||||
}
|
||||
|
||||
$result[] = $properties;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->server->httpResponse->sendStatus(207);
|
||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($result));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is responsible for parsing the request and generating the
|
||||
* response for the CALDAV:free-busy-query REPORT.
|
||||
*
|
||||
* @param DOMNode $dom
|
||||
* @return void
|
||||
*/
|
||||
protected function freeBusyQueryReport(DOMNode $dom) {
|
||||
|
||||
$start = null;
|
||||
$end = null;
|
||||
|
||||
foreach($dom->firstChild->childNodes as $childNode) {
|
||||
|
||||
$clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode);
|
||||
if ($clark == '{' . self::NS_CALDAV . '}time-range') {
|
||||
$start = $childNode->getAttribute('start');
|
||||
$end = $childNode->getAttribute('end');
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if ($start) {
|
||||
$start = Sabre_VObject_DateTimeParser::parseDateTime($start);
|
||||
}
|
||||
if ($end) {
|
||||
$end = Sabre_VObject_DateTimeParser::parseDateTime($end);
|
||||
}
|
||||
|
||||
if (!$start && !$end) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter');
|
||||
}
|
||||
$acl = $this->server->getPlugin('acl');
|
||||
|
||||
if (!$acl) {
|
||||
throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work');
|
||||
}
|
||||
$uri = $this->server->getRequestUri();
|
||||
$acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy');
|
||||
|
||||
$calendar = $this->server->tree->getNodeForPath($uri);
|
||||
if (!$calendar instanceof Sabre_CalDAV_ICalendar) {
|
||||
throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars');
|
||||
}
|
||||
|
||||
$objects = array_map(function($child) {
|
||||
$obj = $child->get();
|
||||
if (is_resource($obj)) {
|
||||
$obj = stream_get_contents($obj);
|
||||
}
|
||||
return $obj;
|
||||
}, $calendar->getChildren());
|
||||
|
||||
$generator = new Sabre_VObject_FreeBusyGenerator();
|
||||
$generator->setObjects($objects);
|
||||
$generator->setTimeRange($start, $end);
|
||||
$result = $generator->getResult();
|
||||
$result = $result->serialize();
|
||||
|
||||
$this->server->httpResponse->sendStatus(200);
|
||||
$this->server->httpResponse->setHeader('Content-Type', 'text/calendar');
|
||||
$this->server->httpResponse->setHeader('Content-Length', strlen($result));
|
||||
$this->server->httpResponse->sendBody($result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is triggered before a file gets updated with new content.
|
||||
*
|
||||
* This plugin uses this method to ensure that CalDAV objects receive
|
||||
* valid calendar data.
|
||||
*
|
||||
* @param string $path
|
||||
* @param Sabre_DAV_IFile $node
|
||||
* @param resource $data
|
||||
* @return void
|
||||
*/
|
||||
public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) {
|
||||
|
||||
if (!$node instanceof Sabre_CalDAV_ICalendarObject)
|
||||
return;
|
||||
|
||||
$this->validateICalendar($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is triggered before a new file is created.
|
||||
*
|
||||
* This plugin uses this method to ensure that newly created calendar
|
||||
* objects contain valid calendar data.
|
||||
*
|
||||
* @param string $path
|
||||
* @param resource $data
|
||||
* @param Sabre_DAV_ICollection $parentNode
|
||||
* @return void
|
||||
*/
|
||||
public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) {
|
||||
|
||||
if (!$parentNode instanceof Sabre_CalDAV_Calendar)
|
||||
return;
|
||||
|
||||
$this->validateICalendar($data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the submitted iCalendar data is in fact, valid.
|
||||
*
|
||||
* An exception is thrown if it's not.
|
||||
*
|
||||
* @param resource|string $data
|
||||
* @return void
|
||||
*/
|
||||
protected function validateICalendar(&$data) {
|
||||
|
||||
// If it's a stream, we convert it to a string first.
|
||||
if (is_resource($data)) {
|
||||
$data = stream_get_contents($data);
|
||||
}
|
||||
|
||||
// Converting the data to unicode, if needed.
|
||||
$data = Sabre_DAV_StringUtil::ensureUTF8($data);
|
||||
|
||||
try {
|
||||
|
||||
$vobj = Sabre_VObject_Reader::read($data);
|
||||
|
||||
} catch (Sabre_VObject_ParseException $e) {
|
||||
|
||||
throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage());
|
||||
|
||||
}
|
||||
|
||||
if ($vobj->name !== 'VCALENDAR') {
|
||||
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.');
|
||||
}
|
||||
|
||||
$foundType = null;
|
||||
$foundUID = null;
|
||||
foreach($vobj->getComponents() as $component) {
|
||||
switch($component->name) {
|
||||
case 'VTIMEZONE' :
|
||||
continue 2;
|
||||
case 'VEVENT' :
|
||||
case 'VTODO' :
|
||||
case 'VJOURNAL' :
|
||||
if (is_null($foundType)) {
|
||||
$foundType = $component->name;
|
||||
if (!isset($component->UID)) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID');
|
||||
}
|
||||
$foundUID = (string)$component->UID;
|
||||
} else {
|
||||
if ($foundType !== $component->name) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType);
|
||||
}
|
||||
if ($foundUID !== (string)$component->UID) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs');
|
||||
}
|
||||
}
|
||||
break;
|
||||
default :
|
||||
throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here');
|
||||
|
||||
}
|
||||
}
|
||||
if (!$foundType)
|
||||
throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles POST requests to the schedule-outbox
|
||||
*
|
||||
* @param Sabre_CalDAV_Schedule_IOutbox $outboxNode
|
||||
* @return void
|
||||
*/
|
||||
public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode) {
|
||||
|
||||
$originator = $this->server->httpRequest->getHeader('Originator');
|
||||
$recipients = $this->server->httpRequest->getHeader('Recipient');
|
||||
|
||||
if (!$originator) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The Originator: header must be specified when making POST requests');
|
||||
}
|
||||
if (!$recipients) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests');
|
||||
}
|
||||
|
||||
if (!preg_match('/^mailto:(.*)@(.*)$/', $originator)) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address');
|
||||
}
|
||||
$originator = substr($originator,7);
|
||||
|
||||
$recipients = explode(',',$recipients);
|
||||
foreach($recipients as $k=>$recipient) {
|
||||
|
||||
$recipient = trim($recipient);
|
||||
if (!preg_match('/^mailto:(.*)@(.*)$/', $recipient)) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address');
|
||||
}
|
||||
$recipient = substr($recipient, 7);
|
||||
$recipients[$k] = $recipient;
|
||||
}
|
||||
|
||||
// We need to make sure that 'originator' matches one of the email
|
||||
// addresses of the selected principal.
|
||||
$principal = $outboxNode->getOwner();
|
||||
$props = $this->server->getProperties($principal,array(
|
||||
'{' . self::NS_CALDAV . '}calendar-user-address-set',
|
||||
));
|
||||
|
||||
$addresses = array();
|
||||
if (isset($props['{' . self::NS_CALDAV . '}calendar-user-address-set'])) {
|
||||
$addresses = $props['{' . self::NS_CALDAV . '}calendar-user-address-set']->getHrefs();
|
||||
}
|
||||
|
||||
if (!in_array('mailto:' . $originator, $addresses)) {
|
||||
throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header');
|
||||
}
|
||||
|
||||
try {
|
||||
$vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true));
|
||||
} catch (Sabre_VObject_ParseException $e) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// Checking for the object type
|
||||
$componentType = null;
|
||||
foreach($vObject->getComponents() as $component) {
|
||||
if ($component->name !== 'VTIMEZONE') {
|
||||
$componentType = $component->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_null($componentType)) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component');
|
||||
}
|
||||
|
||||
// Validating the METHOD
|
||||
$method = strtoupper((string)$vObject->METHOD);
|
||||
if (!$method) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('A METHOD property must be specified in iTIP messages');
|
||||
}
|
||||
|
||||
if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') {
|
||||
$this->iMIPMessage($originator, $recipients, $vObject, $principal);
|
||||
$this->server->httpResponse->sendStatus(200);
|
||||
$this->server->httpResponse->sendBody('Messages sent');
|
||||
} else {
|
||||
throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an iMIP message by email.
|
||||
*
|
||||
* @param string $originator
|
||||
* @param array $recipients
|
||||
* @param Sabre_VObject_Component $vObject
|
||||
* @param string $principal Principal url
|
||||
* @return void
|
||||
*/
|
||||
protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject, $principal) {
|
||||
|
||||
if (!$this->imipHandler) {
|
||||
throw new Sabre_DAV_Exception_NotImplemented('No iMIP handler is setup on this server.');
|
||||
}
|
||||
$this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to generate HTML output for the
|
||||
* Sabre_DAV_Browser_Plugin. This allows us to generate an interface users
|
||||
* can use to create new calendars.
|
||||
*
|
||||
* @param Sabre_DAV_INode $node
|
||||
* @param string $output
|
||||
* @return bool
|
||||
*/
|
||||
public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) {
|
||||
|
||||
if (!$node instanceof Sabre_CalDAV_UserCalendars)
|
||||
return;
|
||||
|
||||
$output.= '<tr><td colspan="2"><form method="post" action="">
|
||||
<h3>Create new calendar</h3>
|
||||
<input type="hidden" name="sabreAction" value="mkcalendar" />
|
||||
<label>Name (uri):</label> <input type="text" name="name" /><br />
|
||||
<label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
|
||||
<input type="submit" value="create" />
|
||||
</form>
|
||||
</td></tr>';
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows us to intercept the 'mkcalendar' sabreAction. This
|
||||
* action enables the user to create new calendars from the browser plugin.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param string $action
|
||||
* @param array $postVars
|
||||
* @return bool
|
||||
*/
|
||||
public function browserPostAction($uri, $action, array $postVars) {
|
||||
|
||||
if ($action!=='mkcalendar')
|
||||
return;
|
||||
|
||||
$resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
|
||||
$properties = array();
|
||||
if (isset($postVars['{DAV:}displayname'])) {
|
||||
$properties['{DAV:}displayname'] = $postVars['{DAV:}displayname'];
|
||||
}
|
||||
$this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
31
dav/SabreDAV/lib/Sabre/CalDAV/Principal/Collection.php
Normal file
31
dav/SabreDAV/lib/Sabre/CalDAV/Principal/Collection.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Principal collection
|
||||
*
|
||||
* This is an alternative collection to the standard ACL principal collection.
|
||||
* This collection adds support for the calendar-proxy-read and
|
||||
* calendar-proxy-write sub-principals, as defined by the caldav-proxy
|
||||
* specification.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Principal_Collection extends Sabre_DAVACL_AbstractPrincipalCollection {
|
||||
|
||||
/**
|
||||
* Returns a child object based on principal information
|
||||
*
|
||||
* @param array $principalInfo
|
||||
* @return Sabre_CalDAV_Principal_User
|
||||
*/
|
||||
public function getChildForPrincipal(array $principalInfo) {
|
||||
|
||||
return new Sabre_CalDAV_Principal_User($this->principalBackend, $principalInfo);
|
||||
|
||||
}
|
||||
|
||||
}
|
178
dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyRead.php
Normal file
178
dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyRead.php
Normal file
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ProxyRead principal
|
||||
*
|
||||
* This class represents a principal group, hosted under the main principal.
|
||||
* This is needed to implement 'Calendar delegation' support. This class is
|
||||
* instantiated by Sabre_CalDAV_Principal_User.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal {
|
||||
|
||||
/**
|
||||
* Principal information from the parent principal.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $principalInfo;
|
||||
|
||||
/**
|
||||
* Principal backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* Creates the object.
|
||||
*
|
||||
* Note that you MUST supply the parent principal information.
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param array $principalInfo
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
|
||||
|
||||
$this->principalInfo = $principalInfo;
|
||||
$this->principalBackend = $principalBackend;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this principals name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return 'calendar-proxy-read';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification time
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the current node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @param string $name The new name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of alternative urls for a principal
|
||||
*
|
||||
* This can for example be an email address, or ldap url.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAlternateUriSet() {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full principal url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrincipalUrl() {
|
||||
|
||||
return $this->principalInfo['uri'] . '/' . $this->getName();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of group members
|
||||
*
|
||||
* If this principal is a group, this function should return
|
||||
* all member principal uri's for the group.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMemberSet() {
|
||||
|
||||
return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of groups this principal is member of
|
||||
*
|
||||
* If this principal is a member of a (list of) groups, this function
|
||||
* should return a list of principal uri's for it's members.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMembership() {
|
||||
|
||||
return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of group members
|
||||
*
|
||||
* If this principal is a group, this method sets all the group members.
|
||||
* The list of members is always overwritten, never appended to.
|
||||
*
|
||||
* This method should throw an exception if the members could not be set.
|
||||
*
|
||||
* @param array $principals
|
||||
* @return void
|
||||
*/
|
||||
public function setGroupMemberSet(array $principals) {
|
||||
|
||||
$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the displayname
|
||||
*
|
||||
* This should be a human readable name for the principal.
|
||||
* If none is available, return the nodename.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName() {
|
||||
|
||||
return $this->getName();
|
||||
|
||||
}
|
||||
|
||||
}
|
178
dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyWrite.php
Normal file
178
dav/SabreDAV/lib/Sabre/CalDAV/Principal/ProxyWrite.php
Normal file
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ProxyWrite principal
|
||||
*
|
||||
* This class represents a principal group, hosted under the main principal.
|
||||
* This is needed to implement 'Calendar delegation' support. This class is
|
||||
* instantiated by Sabre_CalDAV_Principal_User.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal {
|
||||
|
||||
/**
|
||||
* Parent principal information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $principalInfo;
|
||||
|
||||
/**
|
||||
* Principal Backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* Creates the object
|
||||
*
|
||||
* Note that you MUST supply the parent principal information.
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param array $principalInfo
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
|
||||
|
||||
$this->principalInfo = $principalInfo;
|
||||
$this->principalBackend = $principalBackend;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this principals name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return 'calendar-proxy-write';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification time
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the current node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node
|
||||
*
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @param string $name The new name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of alternative urls for a principal
|
||||
*
|
||||
* This can for example be an email address, or ldap url.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAlternateUriSet() {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full principal url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrincipalUrl() {
|
||||
|
||||
return $this->principalInfo['uri'] . '/' . $this->getName();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of group members
|
||||
*
|
||||
* If this principal is a group, this function should return
|
||||
* all member principal uri's for the group.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMemberSet() {
|
||||
|
||||
return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of groups this principal is member of
|
||||
*
|
||||
* If this principal is a member of a (list of) groups, this function
|
||||
* should return a list of principal uri's for it's members.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMembership() {
|
||||
|
||||
return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of group members
|
||||
*
|
||||
* If this principal is a group, this method sets all the group members.
|
||||
* The list of members is always overwritten, never appended to.
|
||||
*
|
||||
* This method should throw an exception if the members could not be set.
|
||||
*
|
||||
* @param array $principals
|
||||
* @return void
|
||||
*/
|
||||
public function setGroupMemberSet(array $principals) {
|
||||
|
||||
$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the displayname
|
||||
*
|
||||
* This should be a human readable name for the principal.
|
||||
* If none is available, return the nodename.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName() {
|
||||
|
||||
return $this->getName();
|
||||
|
||||
}
|
||||
|
||||
}
|
132
dav/SabreDAV/lib/Sabre/CalDAV/Principal/User.php
Normal file
132
dav/SabreDAV/lib/Sabre/CalDAV/Principal/User.php
Normal file
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CalDAV principal
|
||||
*
|
||||
* This is a standard user-principal for CalDAV. This principal is also a
|
||||
* collection and returns the caldav-proxy-read and caldav-proxy-write child
|
||||
* principals.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Principal_User extends Sabre_DAVACL_Principal implements Sabre_DAV_ICollection {
|
||||
|
||||
/**
|
||||
* Creates a new file in the directory
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource $data Initial payload, passed as a readable stream resource.
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($name, $data = null) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory
|
||||
*
|
||||
* @param string $name
|
||||
* @throws Sabre_DAV_Exception_Forbidden
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its name
|
||||
*
|
||||
* @param string $name
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
$principal = $this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name);
|
||||
if (!$principal) {
|
||||
throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found');
|
||||
}
|
||||
if ($name === 'calendar-proxy-read')
|
||||
return new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties);
|
||||
|
||||
if ($name === 'calendar-proxy-write')
|
||||
return new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties);
|
||||
|
||||
throw new Sabre_DAV_Exception_NotFound('Node with name ' . $name . ' was not found');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes
|
||||
*
|
||||
* @return Sabre_DAV_INode[]
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$r = array();
|
||||
if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) {
|
||||
$r[] = new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties);
|
||||
}
|
||||
if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) {
|
||||
$r[] = new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties);
|
||||
}
|
||||
|
||||
return $r;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the child node exists
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
try {
|
||||
$this->getChild($name);
|
||||
return true;
|
||||
} catch (Sabre_DAV_Exception_NotFound $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
$acl = parent::getACL();
|
||||
$acl[] = array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
);
|
||||
$acl[] = array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
);
|
||||
return $acl;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Supported component set property
|
||||
*
|
||||
* This property is a representation of the supported-calendar_component-set
|
||||
* property in the CalDAV namespace. It simply requires an array of components,
|
||||
* such as VEVENT, VTODO
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Property_SupportedCalendarComponentSet extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* List of supported components, such as "VEVENT, VTODO"
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $components;
|
||||
|
||||
/**
|
||||
* Creates the property
|
||||
*
|
||||
* @param array $components
|
||||
*/
|
||||
public function __construct(array $components) {
|
||||
|
||||
$this->components = $components;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of supported components
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValue() {
|
||||
|
||||
return $this->components;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the property in a DOMDocument
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $node
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||
|
||||
$doc = $node->ownerDocument;
|
||||
foreach($this->components as $component) {
|
||||
|
||||
$xcomp = $doc->createElement('cal:comp');
|
||||
$xcomp->setAttribute('name',$component);
|
||||
$node->appendChild($xcomp);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserializes the DOMElement back into a Property class.
|
||||
*
|
||||
* @param DOMElement $node
|
||||
* @return Sabre_CalDAV_Property_SupportedCalendarComponentSet
|
||||
*/
|
||||
static function unserialize(DOMElement $node) {
|
||||
|
||||
$components = array();
|
||||
foreach($node->childNodes as $childNode) {
|
||||
if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)==='{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}comp') {
|
||||
$components[] = $childNode->getAttribute('name');
|
||||
}
|
||||
}
|
||||
return new self($components);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Supported-calendar-data property
|
||||
*
|
||||
* This property is a representation of the supported-calendar-data property
|
||||
* in the CalDAV namespace. SabreDAV only has support for text/calendar;2.0
|
||||
* so the value is currently hardcoded.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Property_SupportedCalendarData extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* Serializes the property in a DOMDocument
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $node
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||
|
||||
$doc = $node->ownerDocument;
|
||||
|
||||
$prefix = isset($server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV])?$server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV]:'cal';
|
||||
|
||||
$caldata = $doc->createElement($prefix . ':calendar-data');
|
||||
$caldata->setAttribute('content-type','text/calendar');
|
||||
$caldata->setAttribute('version','2.0');
|
||||
|
||||
$node->appendChild($caldata);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* supported-collation-set property
|
||||
*
|
||||
* This property is a representation of the supported-collation-set property
|
||||
* in the CalDAV namespace.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Property_SupportedCollationSet extends Sabre_DAV_Property {
|
||||
|
||||
/**
|
||||
* Serializes the property in a DOM document
|
||||
*
|
||||
* @param Sabre_DAV_Server $server
|
||||
* @param DOMElement $node
|
||||
* @return void
|
||||
*/
|
||||
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||
|
||||
$doc = $node->ownerDocument;
|
||||
|
||||
$prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav');
|
||||
if (!$prefix) $prefix = 'cal';
|
||||
|
||||
$node->appendChild(
|
||||
$doc->createElement($prefix . ':supported-collation','i;ascii-casemap')
|
||||
);
|
||||
$node->appendChild(
|
||||
$doc->createElement($prefix . ':supported-collation','i;octet')
|
||||
);
|
||||
$node->appendChild(
|
||||
$doc->createElement($prefix . ':supported-collation','i;unicode-casemap')
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
103
dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IMip.php
Normal file
103
dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IMip.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* iMIP handler.
|
||||
*
|
||||
* This class is responsible for sending out iMIP messages. iMIP is the
|
||||
* email-based transport for iTIP. iTIP deals with scheduling operations for
|
||||
* iCalendar objects.
|
||||
*
|
||||
* If you want to customize the email that gets sent out, you can do so by
|
||||
* extending this class and overriding the sendMessage method.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Schedule_IMip {
|
||||
|
||||
/**
|
||||
* Email address used in From: header.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $senderEmail;
|
||||
|
||||
/**
|
||||
* Creates the email handler.
|
||||
*
|
||||
* @param string $senderEmail. The 'senderEmail' is the email that shows up
|
||||
* in the 'From:' address. This should
|
||||
* generally be some kind of no-reply email
|
||||
* address you own.
|
||||
*/
|
||||
public function __construct($senderEmail) {
|
||||
|
||||
$this->senderEmail = $senderEmail;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends one or more iTip messages through email.
|
||||
*
|
||||
* @param string $originator Originator Email
|
||||
* @param array $recipients Array of email addresses
|
||||
* @param Sabre_VObject_Component $vObject
|
||||
* @param string $principal Principal Url of the originator
|
||||
* @return void
|
||||
*/
|
||||
public function sendMessage($originator, array $recipients, Sabre_VObject_Component $vObject, $principal) {
|
||||
|
||||
foreach($recipients as $recipient) {
|
||||
|
||||
$to = $recipient;
|
||||
$replyTo = $originator;
|
||||
$subject = 'SabreDAV iTIP message';
|
||||
|
||||
switch(strtoupper($vObject->METHOD)) {
|
||||
case 'REPLY' :
|
||||
$subject = 'Response for: ' . $vObject->VEVENT->SUMMARY;
|
||||
break;
|
||||
case 'REQUEST' :
|
||||
$subject = 'Invitation for: ' .$vObject->VEVENT->SUMMARY;
|
||||
break;
|
||||
case 'CANCEL' :
|
||||
$subject = 'Cancelled event: ' . $vObject->VEVENT->SUMMARY;
|
||||
break;
|
||||
}
|
||||
|
||||
$headers = array();
|
||||
$headers[] = 'Reply-To: ' . $replyTo;
|
||||
$headers[] = 'From: ' . $this->senderEmail;
|
||||
$headers[] = 'Content-Type: text/calendar; method=' . (string)$vObject->method . '; charset=utf-8';
|
||||
if (Sabre_DAV_Server::$exposeVersion) {
|
||||
$headers[] = 'X-Sabre-Version: ' . Sabre_DAV_Version::VERSION . '-' . Sabre_DAV_Version::STABILITY;
|
||||
}
|
||||
|
||||
$vcalBody = $vObject->serialize();
|
||||
|
||||
$this->mail($to, $subject, $vcalBody, $headers);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is reponsible for sending the actual email.
|
||||
*
|
||||
* @param string $to Recipient email address
|
||||
* @param string $subject Subject of the email
|
||||
* @param string $body iCalendar body
|
||||
* @param array $headers List of headers
|
||||
* @return void
|
||||
*/
|
||||
protected function mail($to, $subject, $body, array $headers) {
|
||||
|
||||
mail($to, $subject, $body, implode("\r\n", $headers));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
16
dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IOutbox.php
Normal file
16
dav/SabreDAV/lib/Sabre/CalDAV/Schedule/IOutbox.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Implement this interface to have a node be recognized as a CalDAV scheduling
|
||||
* outbox.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_CalDAV_Schedule_IOutbox extends Sabre_DAV_ICollection, Sabre_DAVACL_IACL {
|
||||
|
||||
|
||||
}
|
152
dav/SabreDAV/lib/Sabre/CalDAV/Schedule/Outbox.php
Normal file
152
dav/SabreDAV/lib/Sabre/CalDAV/Schedule/Outbox.php
Normal file
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The CalDAV scheduling outbox
|
||||
*
|
||||
* The outbox is mainly used as an endpoint in the tree for a client to do
|
||||
* free-busy requests. This functionality is completely handled by the
|
||||
* Scheduling plugin, so this object is actually mostly static.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Collection implements Sabre_CalDAV_Schedule_IOutbox {
|
||||
|
||||
/**
|
||||
* The principal Uri
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $principalUri;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $principalUri
|
||||
*/
|
||||
public function __construct($principalUri) {
|
||||
|
||||
$this->principalUri = $principalUri;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the node.
|
||||
*
|
||||
* This is used to generate the url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return 'outbox';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes
|
||||
*
|
||||
* @return Sabre_DAV_INode[]
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->principalUri;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy',
|
||||
'principal' => $this->getOwner(),
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->getOwner(),
|
||||
'protected' => true,
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('You\'re not allowed to update the ACL');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of supported privileges for this node.
|
||||
*
|
||||
* The returned data structure is a list of nested privileges.
|
||||
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
|
||||
* standard structure.
|
||||
*
|
||||
* If null is returned from this method, the default privilege set is used,
|
||||
* which is fine for most common usecases.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getSupportedPrivilegeSet() {
|
||||
|
||||
$default = Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet();
|
||||
$default['aggregates'][] = array(
|
||||
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy',
|
||||
);
|
||||
|
||||
return $default;
|
||||
|
||||
}
|
||||
|
||||
}
|
68
dav/SabreDAV/lib/Sabre/CalDAV/Server.php
Normal file
68
dav/SabreDAV/lib/Sabre/CalDAV/Server.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* CalDAV server
|
||||
*
|
||||
* Deprecated! Warning: This class is now officially deprecated
|
||||
*
|
||||
* This script is a convenience script. It quickly sets up a WebDAV server
|
||||
* with caldav and ACL support, and it creates the root 'principals' and
|
||||
* 'calendars' collections.
|
||||
*
|
||||
* Note that if you plan to do anything moderately complex, you are advised to
|
||||
* not subclass this server, but use Sabre_DAV_Server directly instead. This
|
||||
* class is nothing more than an 'easy setup'.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @deprecated Don't use this class anymore, it will be removed in version 1.7.
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Server extends Sabre_DAV_Server {
|
||||
|
||||
/**
|
||||
* The authentication realm
|
||||
*
|
||||
* Note that if this changes, the hashes in the auth backend must also
|
||||
* be recalculated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $authRealm = 'SabreDAV';
|
||||
|
||||
/**
|
||||
* Sets up the object. A PDO object must be passed to setup all the backends.
|
||||
*
|
||||
* @param PDO $pdo
|
||||
*/
|
||||
public function __construct(PDO $pdo) {
|
||||
|
||||
/* Backends */
|
||||
$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
|
||||
$calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo);
|
||||
$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
|
||||
|
||||
/* Directory structure */
|
||||
$tree = array(
|
||||
new Sabre_CalDAV_Principal_Collection($principalBackend),
|
||||
new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend),
|
||||
);
|
||||
|
||||
/* Initializing server */
|
||||
parent::__construct($tree);
|
||||
|
||||
/* Server Plugins */
|
||||
$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,$this->authRealm);
|
||||
$this->addPlugin($authPlugin);
|
||||
|
||||
$aclPlugin = new Sabre_DAVACL_Plugin();
|
||||
$this->addPlugin($aclPlugin);
|
||||
|
||||
$caldavPlugin = new Sabre_CalDAV_Plugin();
|
||||
$this->addPlugin($caldavPlugin);
|
||||
|
||||
}
|
||||
|
||||
}
|
298
dav/SabreDAV/lib/Sabre/CalDAV/UserCalendars.php
Normal file
298
dav/SabreDAV/lib/Sabre/CalDAV/UserCalendars.php
Normal file
|
@ -0,0 +1,298 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The UserCalenders class contains all calendars associated to one user
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
|
||||
|
||||
/**
|
||||
* Principal backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* CalDAV backend
|
||||
*
|
||||
* @var Sabre_CalDAV_Backend_Abstract
|
||||
*/
|
||||
protected $caldavBackend;
|
||||
|
||||
/**
|
||||
* Principal information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $principalInfo;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
||||
* @param mixed $userUri
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) {
|
||||
|
||||
$this->principalBackend = $principalBackend;
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
$this->principalInfo = $principalBackend->getPrincipalByPath($userUri);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']);
|
||||
return $name;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the name of this object
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function setName($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
throw new Sabre_DAV_Exception_Forbidden();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file under this object.
|
||||
*
|
||||
* This is currently not allowed
|
||||
*
|
||||
* @param string $filename
|
||||
* @param resource $data
|
||||
* @return void
|
||||
*/
|
||||
public function createFile($filename, $data=null) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory under this object.
|
||||
*
|
||||
* This is currently not allowed.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($filename) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single calendar, by name
|
||||
*
|
||||
* @param string $name
|
||||
* @todo needs optimizing
|
||||
* @return Sabre_CalDAV_Calendar
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
foreach($this->getChildren() as $child) {
|
||||
if ($name==$child->getName())
|
||||
return $child;
|
||||
|
||||
}
|
||||
throw new Sabre_DAV_Exception_NotFound('Calendar with name \'' . $name . '\' could not be found');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a calendar exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @todo needs optimizing
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name) {
|
||||
|
||||
foreach($this->getChildren() as $child) {
|
||||
if ($name==$child->getName())
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of calendars
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
|
||||
$objs = array();
|
||||
foreach($calendars as $calendar) {
|
||||
$objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar);
|
||||
}
|
||||
$objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']);
|
||||
return $objs;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $resourceType
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function createExtendedCollection($name, array $resourceType, array $properties) {
|
||||
|
||||
if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) {
|
||||
throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
|
||||
}
|
||||
$this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->principalInfo['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalInfo['uri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->principalInfo['uri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read',
|
||||
'protected' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of supported privileges for this node.
|
||||
*
|
||||
* The returned data structure is a list of nested privileges.
|
||||
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
|
||||
* standard structure.
|
||||
*
|
||||
* If null is returned from this method, the default privilege set is used,
|
||||
* which is fine for most common usecases.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getSupportedPrivilegeSet() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
24
dav/SabreDAV/lib/Sabre/CalDAV/Version.php
Normal file
24
dav/SabreDAV/lib/Sabre/CalDAV/Version.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This class contains the Sabre_CalDAV version constants.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CalDAV_Version {
|
||||
|
||||
/**
|
||||
* Full version number
|
||||
*/
|
||||
const VERSION = '1.7.0';
|
||||
|
||||
/**
|
||||
* Stability : alpha, beta, stable
|
||||
*/
|
||||
const STABILITY = 'alpha';
|
||||
|
||||
}
|
43
dav/SabreDAV/lib/Sabre/CalDAV/includes.php
Normal file
43
dav/SabreDAV/lib/Sabre/CalDAV/includes.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Sabre_CalDAV includes file
|
||||
*
|
||||
* Including this file will automatically include all files from the
|
||||
* Sabre_CalDAV package.
|
||||
*
|
||||
* This often allows faster loadtimes, as autoload-speed is often quite slow.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CalDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
// Begin includes
|
||||
include __DIR__ . '/Backend/Abstract.php';
|
||||
include __DIR__ . '/Backend/PDO.php';
|
||||
include __DIR__ . '/CalendarQueryParser.php';
|
||||
include __DIR__ . '/CalendarQueryValidator.php';
|
||||
include __DIR__ . '/CalendarRootNode.php';
|
||||
include __DIR__ . '/ICalendar.php';
|
||||
include __DIR__ . '/ICalendarObject.php';
|
||||
include __DIR__ . '/ICSExportPlugin.php';
|
||||
include __DIR__ . '/Plugin.php';
|
||||
include __DIR__ . '/Principal/Collection.php';
|
||||
include __DIR__ . '/Principal/ProxyRead.php';
|
||||
include __DIR__ . '/Principal/ProxyWrite.php';
|
||||
include __DIR__ . '/Principal/User.php';
|
||||
include __DIR__ . '/Property/SupportedCalendarComponentSet.php';
|
||||
include __DIR__ . '/Property/SupportedCalendarData.php';
|
||||
include __DIR__ . '/Property/SupportedCollationSet.php';
|
||||
include __DIR__ . '/Schedule/IMip.php';
|
||||
include __DIR__ . '/Schedule/IOutbox.php';
|
||||
include __DIR__ . '/Schedule/Outbox.php';
|
||||
include __DIR__ . '/Server.php';
|
||||
include __DIR__ . '/UserCalendars.php';
|
||||
include __DIR__ . '/Version.php';
|
||||
include __DIR__ . '/Calendar.php';
|
||||
include __DIR__ . '/CalendarObject.php';
|
||||
// End includes
|
312
dav/SabreDAV/lib/Sabre/CardDAV/AddressBook.php
Normal file
312
dav/SabreDAV/lib/Sabre/CardDAV/AddressBook.php
Normal file
|
@ -0,0 +1,312 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The AddressBook class represents a CardDAV addressbook, owned by a specific user
|
||||
*
|
||||
* The AddressBook can contain multiple vcards
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_CardDAV_IAddressBook, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
|
||||
|
||||
/**
|
||||
* This is an array with addressbook information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $addressBookInfo;
|
||||
|
||||
/**
|
||||
* CardDAV backend
|
||||
*
|
||||
* @var Sabre_CardDAV_Backend_Abstract
|
||||
*/
|
||||
private $carddavBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_CardDAV_Backend_Abstract $carddavBackend
|
||||
* @param array $addressBookInfo
|
||||
*/
|
||||
public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend, array $addressBookInfo) {
|
||||
|
||||
$this->carddavBackend = $carddavBackend;
|
||||
$this->addressBookInfo = $addressBookInfo;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the addressbook
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return $this->addressBookInfo['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a card
|
||||
*
|
||||
* @param string $name
|
||||
* @return Sabre_CardDAV_ICard
|
||||
*/
|
||||
public function getChild($name) {
|
||||
|
||||
$obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name);
|
||||
if (!$obj) throw new Sabre_DAV_Exception_NotFound('Card not found');
|
||||
return new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full list of cards
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildren() {
|
||||
|
||||
$objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
|
||||
$children = array();
|
||||
foreach($objs as $obj) {
|
||||
$children[] = new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
|
||||
}
|
||||
return $children;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory
|
||||
*
|
||||
* We actually block this, as subdirectories are not allowed in addressbooks.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function createDirectory($name) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in addressbooks is not allowed');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file
|
||||
*
|
||||
* The contents of the new file must be a valid VCARD.
|
||||
*
|
||||
* This method may return an ETag.
|
||||
*
|
||||
* @param string $name
|
||||
* @param resource $vcardData
|
||||
* @return string|null
|
||||
*/
|
||||
public function createFile($name,$vcardData = null) {
|
||||
|
||||
if (is_resource($vcardData)) {
|
||||
$vcardData = stream_get_contents($vcardData);
|
||||
}
|
||||
// Converting to UTF-8, if needed
|
||||
$vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData);
|
||||
|
||||
return $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the entire addressbook.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the addressbook
|
||||
*
|
||||
* @param string $newName
|
||||
* @return void
|
||||
*/
|
||||
public function setName($newName) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming addressbooks is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date as a unix timestamp.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties on this node,
|
||||
*
|
||||
* The properties array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existent property is always successful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param array $mutations
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateProperties($mutations) {
|
||||
|
||||
return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of properties for this nodes.
|
||||
*
|
||||
* The properties list is a list of propertynames the client requested,
|
||||
* encoded in clark-notation {xmlnamespace}tagname
|
||||
*
|
||||
* If the array is empty, it means 'all properties' were requested.
|
||||
*
|
||||
* @param array $properties
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties($properties) {
|
||||
|
||||
$response = array();
|
||||
foreach($properties as $propertyName) {
|
||||
|
||||
if (isset($this->addressBookInfo[$propertyName])) {
|
||||
|
||||
$response[$propertyName] = $this->addressBookInfo[$propertyName];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->addressBookInfo['principaluri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->addressBookInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->addressBookInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of supported privileges for this node.
|
||||
*
|
||||
* The returned data structure is a list of nested privileges.
|
||||
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
|
||||
* standard structure.
|
||||
*
|
||||
* If null is returned from this method, the default privilege set is used,
|
||||
* which is fine for most common usecases.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getSupportedPrivilegeSet() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
219
dav/SabreDAV/lib/Sabre/CardDAV/AddressBookQueryParser.php
Normal file
219
dav/SabreDAV/lib/Sabre/CardDAV/AddressBookQueryParser.php
Normal file
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Parses the addressbook-query report request body.
|
||||
*
|
||||
* Whoever designed this format, and the CalDAV equivalent even more so,
|
||||
* has no feel for design.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CardDAV_AddressBookQueryParser {
|
||||
|
||||
const TEST_ANYOF = 'anyof';
|
||||
const TEST_ALLOF = 'allof';
|
||||
|
||||
/**
|
||||
* List of requested properties the client wanted
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $requestedProperties;
|
||||
|
||||
/**
|
||||
* The number of results the client wants
|
||||
*
|
||||
* null means it wasn't specified, which in most cases means 'all results'.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $limit;
|
||||
|
||||
/**
|
||||
* List of property filters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $filters;
|
||||
|
||||
/**
|
||||
* Either TEST_ANYOF or TEST_ALLOF
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $test;
|
||||
|
||||
/**
|
||||
* DOM Document
|
||||
*
|
||||
* @var DOMDocument
|
||||
*/
|
||||
protected $dom;
|
||||
|
||||
/**
|
||||
* DOM XPath object
|
||||
*
|
||||
* @var DOMXPath
|
||||
*/
|
||||
protected $xpath;
|
||||
|
||||
/**
|
||||
* Creates the parser
|
||||
*
|
||||
* @param DOMDocument $dom
|
||||
*/
|
||||
public function __construct(DOMDocument $dom) {
|
||||
|
||||
$this->dom = $dom;
|
||||
|
||||
$this->xpath = new DOMXPath($dom);
|
||||
$this->xpath->registerNameSpace('card',Sabre_CardDAV_Plugin::NS_CARDDAV);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function parse() {
|
||||
|
||||
$filterNode = null;
|
||||
|
||||
$limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)');
|
||||
if (is_nan($limit)) $limit = null;
|
||||
|
||||
$filter = $this->xpath->query('/card:addressbook-query/card:filter');
|
||||
|
||||
// According to the CardDAV spec there needs to be exactly 1 filter
|
||||
// element. However, KDE 4.8.2 contains a bug that will encode 0 filter
|
||||
// elements, so this is a workaround for that.
|
||||
//
|
||||
// See: https://bugs.kde.org/show_bug.cgi?id=300047
|
||||
if ($filter->length === 0) {
|
||||
$test = null;
|
||||
$filter = null;
|
||||
} elseif ($filter->length === 1) {
|
||||
$filter = $filter->item(0);
|
||||
$test = $this->xpath->evaluate('string(@test)', $filter);
|
||||
} else {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
|
||||
}
|
||||
|
||||
if (!$test) $test = self::TEST_ANYOF;
|
||||
if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
|
||||
}
|
||||
|
||||
$propFilters = array();
|
||||
|
||||
$propFilterNodes = $this->xpath->query('card:prop-filter', $filter);
|
||||
for($ii=0; $ii < $propFilterNodes->length; $ii++) {
|
||||
|
||||
$propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii));
|
||||
|
||||
|
||||
}
|
||||
|
||||
$this->filters = $propFilters;
|
||||
$this->limit = $limit;
|
||||
$this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
|
||||
$this->test = $test;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the prop-filter xml element
|
||||
*
|
||||
* @param DOMElement $propFilterNode
|
||||
* @return array
|
||||
*/
|
||||
protected function parsePropFilterNode(DOMElement $propFilterNode) {
|
||||
|
||||
$propFilter = array();
|
||||
$propFilter['name'] = $propFilterNode->getAttribute('name');
|
||||
$propFilter['test'] = $propFilterNode->getAttribute('test');
|
||||
if (!$propFilter['test']) $propFilter['test'] = 'anyof';
|
||||
|
||||
$propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0;
|
||||
|
||||
$paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode);
|
||||
|
||||
$propFilter['param-filters'] = array();
|
||||
|
||||
|
||||
for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
|
||||
|
||||
$propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii));
|
||||
|
||||
}
|
||||
$propFilter['text-matches'] = array();
|
||||
$textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode);
|
||||
|
||||
for($ii=0;$ii<$textMatchNodes->length;$ii++) {
|
||||
|
||||
$propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii));
|
||||
|
||||
}
|
||||
|
||||
return $propFilter;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the param-filter element
|
||||
*
|
||||
* @param DOMElement $paramFilterNode
|
||||
* @return array
|
||||
*/
|
||||
public function parseParamFilterNode(DOMElement $paramFilterNode) {
|
||||
|
||||
$paramFilter = array();
|
||||
$paramFilter['name'] = $paramFilterNode->getAttribute('name');
|
||||
$paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0;
|
||||
$paramFilter['text-match'] = null;
|
||||
|
||||
$textMatch = $this->xpath->query('card:text-match', $paramFilterNode);
|
||||
if ($textMatch->length>0) {
|
||||
$paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0));
|
||||
}
|
||||
|
||||
return $paramFilter;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Text match
|
||||
*
|
||||
* @param DOMElement $textMatchNode
|
||||
* @return array
|
||||
*/
|
||||
public function parseTextMatchNode(DOMElement $textMatchNode) {
|
||||
|
||||
$matchType = $textMatchNode->getAttribute('match-type');
|
||||
if (!$matchType) $matchType = 'contains';
|
||||
|
||||
if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) {
|
||||
throw new Sabre_DAV_Exception_BadRequest('Unknown match-type: ' . $matchType);
|
||||
}
|
||||
|
||||
$negateCondition = $textMatchNode->getAttribute('negate-condition');
|
||||
$negateCondition = $negateCondition==='yes';
|
||||
$collation = $textMatchNode->getAttribute('collation');
|
||||
if (!$collation) $collation = 'i;unicode-casemap';
|
||||
|
||||
return array(
|
||||
'negate-condition' => $negateCondition,
|
||||
'collation' => $collation,
|
||||
'match-type' => $matchType,
|
||||
'value' => $textMatchNode->nodeValue
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
78
dav/SabreDAV/lib/Sabre/CardDAV/AddressBookRoot.php
Normal file
78
dav/SabreDAV/lib/Sabre/CardDAV/AddressBookRoot.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* AddressBook rootnode
|
||||
*
|
||||
* This object lists a collection of users, which can contain addressbooks.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CardDAV_AddressBookRoot extends Sabre_DAVACL_AbstractPrincipalCollection {
|
||||
|
||||
/**
|
||||
* Principal Backend
|
||||
*
|
||||
* @var Sabre_DAVACL_IPrincipalBackend
|
||||
*/
|
||||
protected $principalBackend;
|
||||
|
||||
/**
|
||||
* CardDAV backend
|
||||
*
|
||||
* @var Sabre_CardDAV_Backend_Abstract
|
||||
*/
|
||||
protected $carddavBackend;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* This constructor needs both a principal and a carddav backend.
|
||||
*
|
||||
* By default this class will show a list of addressbook collections for
|
||||
* principals in the 'principals' collection. If your main principals are
|
||||
* actually located in a different path, use the $principalPrefix argument
|
||||
* to override this.
|
||||
*
|
||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||
* @param Sabre_CardDAV_Backend_Abstract $carddavBackend
|
||||
* @param string $principalPrefix
|
||||
*/
|
||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalPrefix = 'principals') {
|
||||
|
||||
$this->carddavBackend = $carddavBackend;
|
||||
parent::__construct($principalBackend, $principalPrefix);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the node
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a node for a principal.
|
||||
*
|
||||
* The passed array contains principal information, and is guaranteed to
|
||||
* at least contain a uri item. Other properties may or may not be
|
||||
* supplied by the authentication backend.
|
||||
*
|
||||
* @param array $principal
|
||||
* @return Sabre_DAV_INode
|
||||
*/
|
||||
public function getChildForPrincipal(array $principal) {
|
||||
|
||||
return new Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']);
|
||||
|
||||
}
|
||||
|
||||
}
|
166
dav/SabreDAV/lib/Sabre/CardDAV/Backend/Abstract.php
Normal file
166
dav/SabreDAV/lib/Sabre/CardDAV/Backend/Abstract.php
Normal file
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Abstract Backend class
|
||||
*
|
||||
* This class serves as a base-class for addressbook backends
|
||||
*
|
||||
* Note that there are references to 'addressBookId' scattered throughout the
|
||||
* class. The value of the addressBookId is completely up to you, it can be any
|
||||
* arbitrary value you can use as an unique identifier.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Sabre_CardDAV_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* Returns the list of addressbooks for a specific user.
|
||||
*
|
||||
* Every addressbook should have the following properties:
|
||||
* id - an arbitrary unique id
|
||||
* uri - the 'basename' part of the url
|
||||
* principaluri - Same as the passed parameter
|
||||
*
|
||||
* Any additional clark-notation property may be passed besides this. Some
|
||||
* common ones are :
|
||||
* {DAV:}displayname
|
||||
* {urn:ietf:params:xml:ns:carddav}addressbook-description
|
||||
* {http://calendarserver.org/ns/}getctag
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public abstract function getAddressBooksForUser($principalUri);
|
||||
|
||||
/**
|
||||
* Updates an addressbook's properties
|
||||
*
|
||||
* See Sabre_DAV_IProperties for a description of the mutations array, as
|
||||
* well as the return value.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param array $mutations
|
||||
* @see Sabre_DAV_IProperties::updateProperties
|
||||
* @return bool|array
|
||||
*/
|
||||
public abstract function updateAddressBook($addressBookId, array $mutations);
|
||||
|
||||
/**
|
||||
* Creates a new address book
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $url Just the 'basename' of the url.
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
abstract public function createAddressBook($principalUri, $url, array $properties);
|
||||
|
||||
/**
|
||||
* Deletes an entire addressbook and all its contents
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @return void
|
||||
*/
|
||||
abstract public function deleteAddressBook($addressBookId);
|
||||
|
||||
/**
|
||||
* Returns all cards for a specific addressbook id.
|
||||
*
|
||||
* This method should return the following properties for each card:
|
||||
* * carddata - raw vcard data
|
||||
* * uri - Some unique url
|
||||
* * lastmodified - A unix timestamp
|
||||
*
|
||||
* It's recommended to also return the following properties:
|
||||
* * etag - A unique etag. This must change every time the card changes.
|
||||
* * size - The size of the card in bytes.
|
||||
*
|
||||
* If these last two properties are provided, less time will be spent
|
||||
* calculating them. If they are specified, you can also ommit carddata.
|
||||
* This may speed up certain requests, especially with large cards.
|
||||
*
|
||||
* @param mixed $addressbookId
|
||||
* @return array
|
||||
*/
|
||||
public abstract function getCards($addressbookId);
|
||||
|
||||
/**
|
||||
* Returns a specfic card.
|
||||
*
|
||||
* The same set of properties must be returned as with getCards. The only
|
||||
* exception is that 'carddata' is absolutely required.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @return array
|
||||
*/
|
||||
public abstract function getCard($addressBookId, $cardUri);
|
||||
|
||||
/**
|
||||
* Creates a new card.
|
||||
*
|
||||
* The addressbook id will be passed as the first argument. This is the
|
||||
* same id as it is returned from the getAddressbooksForUser method.
|
||||
*
|
||||
* The cardUri is a base uri, and doesn't include the full path. The
|
||||
* cardData argument is the vcard body, and is passed as a string.
|
||||
*
|
||||
* It is possible to return an ETag from this method. This ETag is for the
|
||||
* newly created resource, and must be enclosed with double quotes (that
|
||||
* is, the string itself must contain the double quotes).
|
||||
*
|
||||
* You should only return the ETag if you store the carddata as-is. If a
|
||||
* subsequent GET request on the same card does not have the same body,
|
||||
* byte-by-byte and you did return an ETag here, clients tend to get
|
||||
* confused.
|
||||
*
|
||||
* If you don't return an ETag, you can just return null.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @param string $cardData
|
||||
* @return string|null
|
||||
*/
|
||||
abstract public function createCard($addressBookId, $cardUri, $cardData);
|
||||
|
||||
/**
|
||||
* Updates a card.
|
||||
*
|
||||
* The addressbook id will be passed as the first argument. This is the
|
||||
* same id as it is returned from the getAddressbooksForUser method.
|
||||
*
|
||||
* The cardUri is a base uri, and doesn't include the full path. The
|
||||
* cardData argument is the vcard body, and is passed as a string.
|
||||
*
|
||||
* It is possible to return an ETag from this method. This ETag should
|
||||
* match that of the updated resource, and must be enclosed with double
|
||||
* quotes (that is: the string itself must contain the actual quotes).
|
||||
*
|
||||
* You should only return the ETag if you store the carddata as-is. If a
|
||||
* subsequent GET request on the same card does not have the same body,
|
||||
* byte-by-byte and you did return an ETag here, clients tend to get
|
||||
* confused.
|
||||
*
|
||||
* If you don't return an ETag, you can just return null.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @param string $cardData
|
||||
* @return string|null
|
||||
*/
|
||||
abstract public function updateCard($addressBookId, $cardUri, $cardData);
|
||||
|
||||
/**
|
||||
* Deletes a card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function deleteCard($addressBookId, $cardUri);
|
||||
|
||||
}
|
330
dav/SabreDAV/lib/Sabre/CardDAV/Backend/PDO.php
Normal file
330
dav/SabreDAV/lib/Sabre/CardDAV/Backend/PDO.php
Normal file
|
@ -0,0 +1,330 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PDO CardDAV backend
|
||||
*
|
||||
* This CardDAV backend uses PDO to store addressbooks
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract {
|
||||
|
||||
/**
|
||||
* PDO connection
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* The PDO table name used to store addressbooks
|
||||
*/
|
||||
protected $addressBooksTableName;
|
||||
|
||||
/**
|
||||
* The PDO table name used to store cards
|
||||
*/
|
||||
protected $cardsTableName;
|
||||
|
||||
/**
|
||||
* Sets up the object
|
||||
*
|
||||
* @param PDO $pdo
|
||||
* @param string $addressBooksTableName
|
||||
* @param string $cardsTableName
|
||||
*/
|
||||
public function __construct(PDO $pdo, $addressBooksTableName = 'addressbooks', $cardsTableName = 'cards') {
|
||||
|
||||
$this->pdo = $pdo;
|
||||
$this->addressBooksTableName = $addressBooksTableName;
|
||||
$this->cardsTableName = $cardsTableName;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of addressbooks for a specific user.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public function getAddressBooksForUser($principalUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM '.$this->addressBooksTableName.' WHERE principaluri = ?');
|
||||
$stmt->execute(array($principalUri));
|
||||
|
||||
$addressBooks = array();
|
||||
|
||||
foreach($stmt->fetchAll() as $row) {
|
||||
|
||||
$addressBooks[] = array(
|
||||
'id' => $row['id'],
|
||||
'uri' => $row['uri'],
|
||||
'principaluri' => $row['principaluri'],
|
||||
'{DAV:}displayname' => $row['displayname'],
|
||||
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
|
||||
'{http://calendarserver.org/ns/}getctag' => $row['ctag'],
|
||||
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}supported-address-data' =>
|
||||
new Sabre_CardDAV_Property_SupportedAddressData(),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return $addressBooks;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates an addressbook's properties
|
||||
*
|
||||
* See Sabre_DAV_IProperties for a description of the mutations array, as
|
||||
* well as the return value.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param array $mutations
|
||||
* @see Sabre_DAV_IProperties::updateProperties
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateAddressBook($addressBookId, array $mutations) {
|
||||
|
||||
$updates = array();
|
||||
|
||||
foreach($mutations as $property=>$newValue) {
|
||||
|
||||
switch($property) {
|
||||
case '{DAV:}displayname' :
|
||||
$updates['displayname'] = $newValue;
|
||||
break;
|
||||
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
|
||||
$updates['description'] = $newValue;
|
||||
break;
|
||||
default :
|
||||
// If any unsupported values were being updated, we must
|
||||
// let the entire request fail.
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// No values are being updated?
|
||||
if (!$updates) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = 'UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 ';
|
||||
foreach($updates as $key=>$value) {
|
||||
$query.=', `' . $key . '` = :' . $key . ' ';
|
||||
}
|
||||
$query.=' WHERE id = :addressbookid';
|
||||
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$updates['addressbookid'] = $addressBookId;
|
||||
|
||||
$stmt->execute($updates);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new address book
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $url Just the 'basename' of the url.
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function createAddressBook($principalUri, $url, array $properties) {
|
||||
|
||||
$values = array(
|
||||
'displayname' => null,
|
||||
'description' => null,
|
||||
'principaluri' => $principalUri,
|
||||
'uri' => $url,
|
||||
);
|
||||
|
||||
foreach($properties as $property=>$newValue) {
|
||||
|
||||
switch($property) {
|
||||
case '{DAV:}displayname' :
|
||||
$values['displayname'] = $newValue;
|
||||
break;
|
||||
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
|
||||
$values['description'] = $newValue;
|
||||
break;
|
||||
default :
|
||||
throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)';
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$stmt->execute($values);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entire addressbook and all its contents
|
||||
*
|
||||
* @param int $addressBookId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAddressBook($addressBookId) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?');
|
||||
$stmt->execute(array($addressBookId));
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?');
|
||||
$stmt->execute(array($addressBookId));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all cards for a specific addressbook id.
|
||||
*
|
||||
* This method should return the following properties for each card:
|
||||
* * carddata - raw vcard data
|
||||
* * uri - Some unique url
|
||||
* * lastmodified - A unix timestamp
|
||||
*
|
||||
* It's recommended to also return the following properties:
|
||||
* * etag - A unique etag. This must change every time the card changes.
|
||||
* * size - The size of the card in bytes.
|
||||
*
|
||||
* If these last two properties are provided, less time will be spent
|
||||
* calculating them. If they are specified, you can also ommit carddata.
|
||||
* This may speed up certain requests, especially with large cards.
|
||||
*
|
||||
* @param mixed $addressbookId
|
||||
* @return array
|
||||
*/
|
||||
public function getCards($addressbookId) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?');
|
||||
$stmt->execute(array($addressbookId));
|
||||
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specfic card.
|
||||
*
|
||||
* The same set of properties must be returned as with getCards. The only
|
||||
* exception is that 'carddata' is absolutely required.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCard($addressBookId, $cardUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1');
|
||||
$stmt->execute(array($addressBookId, $cardUri));
|
||||
|
||||
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return (count($result)>0?$result[0]:false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new card.
|
||||
*
|
||||
* The addressbook id will be passed as the first argument. This is the
|
||||
* same id as it is returned from the getCards method.
|
||||
*
|
||||
* The cardUri is a base uri, and doesn't include the full path. The
|
||||
* cardData argument is the vcard body, and is passed as a string.
|
||||
*
|
||||
* It is possible to return an ETag from this method. This ETag is for the
|
||||
* newly created resource, and must be enclosed with double quotes (that
|
||||
* is, the string itself must contain the double quotes).
|
||||
*
|
||||
* You should only return the ETag if you store the carddata as-is. If a
|
||||
* subsequent GET request on the same card does not have the same body,
|
||||
* byte-by-byte and you did return an ETag here, clients tend to get
|
||||
* confused.
|
||||
*
|
||||
* If you don't return an ETag, you can just return null.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @param string $cardData
|
||||
* @return string|null
|
||||
*/
|
||||
public function createCard($addressBookId, $cardUri, $cardData) {
|
||||
|
||||
$stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)');
|
||||
|
||||
$result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId));
|
||||
|
||||
$stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt2->execute(array($addressBookId));
|
||||
|
||||
return '"' . md5($cardData) . '"';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a card.
|
||||
*
|
||||
* The addressbook id will be passed as the first argument. This is the
|
||||
* same id as it is returned from the getAddressbooksForUser method.
|
||||
*
|
||||
* The cardUri is a base uri, and doesn't include the full path. The
|
||||
* cardData argument is the vcard body, and is passed as a string.
|
||||
*
|
||||
* It is possible to return an ETag from this method. This ETag should
|
||||
* match that of the updated resource, and must be enclosed with double
|
||||
* quotes (that is: the string itself must contain the actual quotes).
|
||||
*
|
||||
* You should only return the ETag if you store the carddata as-is. If a
|
||||
* subsequent GET request on the same card does not have the same body,
|
||||
* byte-by-byte and you did return an ETag here, clients tend to get
|
||||
* confused.
|
||||
*
|
||||
* If you don't return an ETag, you can just return null.
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @param string $cardData
|
||||
* @return string|null
|
||||
*/
|
||||
public function updateCard($addressBookId, $cardUri, $cardData) {
|
||||
|
||||
$stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?');
|
||||
$stmt->execute(array($cardData, time(), $cardUri, $addressBookId));
|
||||
|
||||
$stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt2->execute(array($addressBookId));
|
||||
|
||||
return '"' . md5($cardData) . '"';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a card
|
||||
*
|
||||
* @param mixed $addressBookId
|
||||
* @param string $cardUri
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteCard($addressBookId, $cardUri) {
|
||||
|
||||
$stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?');
|
||||
$stmt->execute(array($addressBookId, $cardUri));
|
||||
|
||||
$stmt2 = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET ctag = ctag + 1 WHERE id = ?');
|
||||
$stmt2->execute(array($addressBookId));
|
||||
|
||||
return $stmt->rowCount()===1;
|
||||
|
||||
}
|
||||
}
|
250
dav/SabreDAV/lib/Sabre/CardDAV/Card.php
Normal file
250
dav/SabreDAV/lib/Sabre/CardDAV/Card.php
Normal file
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* The Card object represents a single Card from an addressbook
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, Sabre_DAVACL_IACL {
|
||||
|
||||
/**
|
||||
* CardDAV backend
|
||||
*
|
||||
* @var Sabre_CardDAV_Backend_Abstract
|
||||
*/
|
||||
private $carddavBackend;
|
||||
|
||||
/**
|
||||
* Array with information about this Card
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $cardData;
|
||||
|
||||
/**
|
||||
* Array with information about the containing addressbook
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $addressBookInfo;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Sabre_CardDAV_Backend_Abstract $carddavBackend
|
||||
* @param array $addressBookInfo
|
||||
* @param array $cardData
|
||||
*/
|
||||
public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo,array $cardData) {
|
||||
|
||||
$this->carddavBackend = $carddavBackend;
|
||||
$this->addressBookInfo = $addressBookInfo;
|
||||
$this->cardData = $cardData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uri for this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
|
||||
return $this->cardData['uri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the VCard-formatted object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get() {
|
||||
|
||||
// Pre-populating 'carddata' is optional. If we don't yet have it
|
||||
// already, we fetch it from the backend.
|
||||
if (!isset($this->cardData['carddata'])) {
|
||||
$this->cardData = $this->carddavBackend->getCard($this->addressBookInfo['id'], $this->cardData['uri']);
|
||||
}
|
||||
return $this->cardData['carddata'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the VCard-formatted object
|
||||
*
|
||||
* @param string $cardData
|
||||
* @return string|null
|
||||
*/
|
||||
public function put($cardData) {
|
||||
|
||||
if (is_resource($cardData))
|
||||
$cardData = stream_get_contents($cardData);
|
||||
|
||||
// Converting to UTF-8, if needed
|
||||
$cardData = Sabre_DAV_StringUtil::ensureUTF8($cardData);
|
||||
|
||||
$etag = $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData);
|
||||
$this->cardData['carddata'] = $cardData;
|
||||
$this->cardData['etag'] = $etag;
|
||||
|
||||
return $etag;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the card
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime content-type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType() {
|
||||
|
||||
return 'text/x-vcard; charset=utf-8';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ETag for this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getETag() {
|
||||
|
||||
if (isset($this->cardData['etag'])) {
|
||||
return $this->cardData['etag'];
|
||||
} else {
|
||||
return '"' . md5($this->get()) . '"';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification date as a unix timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified() {
|
||||
|
||||
return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of this object in bytes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize() {
|
||||
|
||||
if (array_key_exists('size', $this->cardData)) {
|
||||
return $this->cardData['size'];
|
||||
} else {
|
||||
return strlen($this->get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
|
||||
return $this->addressBookInfo['principaluri'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a group principal
|
||||
*
|
||||
* This must be a url to a principal, or null if there's no owner
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGroup() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of ACE's for this node.
|
||||
*
|
||||
* Each ACE has the following properties:
|
||||
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||
* currently the only supported privileges
|
||||
* * 'principal', a url to the principal who owns the node
|
||||
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||
* be updated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getACL() {
|
||||
|
||||
return array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => $this->addressBookInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => $this->addressBookInfo['principaluri'],
|
||||
'protected' => true,
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ACL
|
||||
*
|
||||
* This method will receive a list of new ACE's.
|
||||
*
|
||||
* @param array $acl
|
||||
* @return void
|
||||
*/
|
||||
public function setACL(array $acl) {
|
||||
|
||||
throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of supported privileges for this node.
|
||||
*
|
||||
* The returned data structure is a list of nested privileges.
|
||||
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
|
||||
* standard structure.
|
||||
*
|
||||
* If null is returned from this method, the default privilege set is used,
|
||||
* which is fine for most common usecases.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getSupportedPrivilegeSet() {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
18
dav/SabreDAV/lib/Sabre/CardDAV/IAddressBook.php
Normal file
18
dav/SabreDAV/lib/Sabre/CardDAV/IAddressBook.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* AddressBook interface
|
||||
*
|
||||
* Implement this interface to allow a node to be recognized as an addressbook.
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage CardDAV
|
||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface Sabre_CardDAV_IAddressBook extends Sabre_DAV_ICollection {
|
||||
|
||||
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue