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
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