pax_global_header00006660000000000000000000000064126703567540014531gustar00rootroot0000000000000052 comment=9f8c1939a3f66eb7170489fc48579ffd1461af62 sabre-dav-2.1.10/000077500000000000000000000000001267035675400134565ustar00rootroot00000000000000sabre-dav-2.1.10/.gitignore000066400000000000000000000006121267035675400154450ustar00rootroot00000000000000# Unit tests tests/temp tests/.sabredav tests/cov # ViM *.swp # Composer composer.lock vendor # Composer binaries bin/phing bin/phpunit bin/vobject bin/generate_vcards bin/phpdocmd bin/phpunit bin/phpcs # Assuming every .php file in the root is for testing /*.php # Other testing stuff /tmpdata /data /public # Build build build.properties # Docs docs/api docs/wikidocs # Mac .DS_Store sabre-dav-2.1.10/.travis.yml000066400000000000000000000011261267035675400155670ustar00rootroot00000000000000language: php php: - 5.4 - 5.5 - 5.6 - 7 matrix: fast_finish: true env: matrix: - LOWEST_DEPS="" TEST_DEPS="" - LOWEST_DEPS="--prefer-lowest" TEST_DEPS="tests/Sabre/" services: - mysql before_script: - mysql -e 'create database sabredav' # - composer self-update - composer update --prefer-source $LOWEST_DEPS script: - ./bin/phpunit --configuration tests/phpunit.xml $TEST_DEPS - ./bin/phpcs -p --standard=tests/phpcs/ruleset.xml lib/ notifications: irc: channels: - "chat.freenode.net#sabredav" on_success: change on_failure: change sabre-dav-2.1.10/CONTRIBUTING.md000066400000000000000000000057061267035675400157170ustar00rootroot00000000000000Contributing to sabre projects ============================== Want to contribute to sabre/dav? Here are some guidelines to ensure your patch gets accepted. Building a new feature? Contact us first ---------------------------------------- We may not want to accept every feature that comes our way. Sometimes features are out of scope for our projects. We don't want to waste your time, so by having a quick chat with us first, you may find out quickly if the feature makes sense to us, and we can give some tips on how to best build the feature. If we don't accept the feature, it could be for a number of reasons. For instance, we've rejected features in the past because we felt uncomfortable assuming responsibility for maintaining the feature. In those cases, it's often possible to keep the feature separate from the sabre projects. sabre/dav for instance has a plugin system, and there's no reason the feature can't live in a project you own. In that case, definitely let us know about your plugin as well, so we can feature it on [sabre.io][4]. We are often on [IRC][5], in the #sabredav channel on freenode. If there's no one there, post a message on the [mailing list][6]. Coding standards ---------------- sabre projects follow: 1. [PSR-1][1] 2. [PSR-4][2] sabre projects don't follow [PSR-2][3]. In addition to that, here's a list of basic rules: 1. PHP 5.4 array syntax must be used every where. This means you use `[` and `]` instead of `array(` and `)`. 2. Use PHP namespaces everywhere. 3. Use 4 spaces for indentiation. 4. Try to keep your lines under 80 characters. This is not a hard rule, as there are many places in the source where it felt more sensibile to not do so. In particular, function declarations are never split over multiple lines. 5. Opening braces (`{`) are _always_ on the same line as the `class`, `if`, `function`, etc. they belong to. 6. `public` must be omitted from method declarations. It must also be omitted for static properties. 7. All files should use unix-line endings (`\n`). 8. Files must omit the closing php tag (`?>`). 9. `true`, `false` and `null` are always lower-case. 10. Constants are always upper-case. 11. Any of the rules stated before may be broken where this is the pragmatic thing to do. Unit test requirements ---------------------- Any new feature or change requires unittests. We use [PHPUnit][7] for all our tests. Adding unittests will greatly increase the likelyhood of us quickly accepting your pull request. If unittests are not included though for whatever reason, we'd still _love_ your pull request. We may have to write the tests ourselves, which can increase the time it takes to accept the patch, but we'd still really like your contribution! [1]: http://www.php-fig.org/psr/psr-1/ [2]: http://www.php-fig.org/psr/psr-4/ [3]: http://www.php-fig.org/psr/psr-2/ [4]: http://sabre.io/ [5]: irc://freenode.net/#sabredav [6]: http://groups.google.com/group/sabredav-discuss [7]: http://phpunit.de/ sabre-dav-2.1.10/ChangeLog.md000066400000000000000000002253041267035675400156350ustar00rootroot00000000000000ChangeLog ========= 2.1.10 (2016-03-10) ------------------- * #784: Sync logs for address books were not correctly cleaned up after deleting them. 2.1.9 (2016-01-25) ------------------ * #674: PHP7 support (@DeepDiver1975). * The zip release ships with [sabre/vobject 3.5.0][vobj], [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt]. 2.1.8 (2016-01-04) ------------------ * #729: Fixed a caching problem in the Tree object. * #740: Bugs in `migrate20.php` script. * The zip release ships with [sabre/vobject 3.4.8][vobj], [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt]. 2.1.7 (2015-09-05) ------------------ * #705: A `MOVE` request that gets prevented from deleting the source resource will still remove the target resource. Now all events are triggered before any destructive operations. * The zip release ships with [sabre/vobject 3.4.7][vobj], [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt]. 2.1.6 (2015-07-21) ------------------ * #657: Migration script would break when coming a cross an iCalendar object with no UID. * #691: Workaround for broken Windows Phone client. * The zip release ships with [sabre/vobject 3.4.5][vobj], [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt]. 2.1.5 (2015-07-11) ------------------ * #677: Resources with the name '0' would not get retrieved when using `Depth: infinity` in a `PROPFIND` request. * The zip release ships with [sabre/vobject 3.4.5][vobj], [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt]. 2.1.4 (2015-05-25) ------------------ * #651: Double-encoded path in the browser plugin. Should fix a few broken links in some setups. * #650: Correctly cleaning up change info after deleting calendars (@ErrOrnAmE). * #658: Updating `schedule-calendar-default-URL` does not work well, so we're disabling it until there's a better fix. * The zip release ships with [sabre/vobject 3.4.3][vobj], [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt]. 2.1.3 (2015-02-25) ------------------ * #586: `SCHEDULE-STATUS` should not contain a reason-phrase. * #539: Fixed a bug related to scheduling in shared calendars. * #595: Support for calendar-timezone in iCalendar exports. * #581: findByUri would send empty prefixes to the principal backend (@soydeedo) * #611: Escaping a bit more HTML output in the browser plugin. (@LukasReschke) * #610: Don't allow discovery of arbitrary files using `..` in the browser plugin (@LukasReschke). * Browser plugin now shows quota properties. * #612: PropertyStorage didn't delete properties from nodes when a node's parents get deleted. * #581: Fixed problems related to finding attendee information during scheduling. * The zip release ships with [sabre/vobject 3.4.2][vobj], [sabre/http 3.0.4][http], and [sabre/event 2.0.1][evnt]. 2.1.2 (2014-12-10) ------------------ * #566: Another issue related to the migration script, which would cause scheduling to not work well for events that were already added before the migration. * #567: Doing freebusy requests on accounts that had 0 calendars would throw a `E_NOTICE`. * #579: Browser plugin can throw exception for a few resourcetypes that didn't have an icon defined. * The zip release ships with [sabre/vobject 3.3.4][vobj], [sabre/http 3.0.4][http], and [sabre/event 2.0.1][evnt]. 2.1.1 (2014-11-22) ------------------ * #561: IMip Plugin didn't strip mailto: from email addresses. * #566: Migration process had 2 problems related to adding the `uid` field to the `calendarobjects` table. * The zip release ships with [sabre/vobject 3.3.4][vobj], [sabre/http 3.0.2][http], and [sabre/event 2.0.1][evnt]. 2.1.0 (2014-11-19) ------------------ * #541: CalDAV PDO backend didn't respect overridden PDO table names. * #550: Scheduling invites are no longer delivered into shared calendars. * #554: `calendar-multiget` `REPORT` did not work on inbox items. * #555: The `calendar-timezone` property is now respected for floating times and all-day events in the `calendar-query`, `calendar-multiget` and `free-busy-query` REPORTs. * #555: The `calendar-timezone` property is also respected for scheduling free-busy requests. * #547: CalDAV system too aggressively 'corrects' incoming iCalendar data, and as a result doesn't return an etag for common cases. * The zip release ships with [sabre/vobject 3.3.4][vobj], [sabre/http 3.0.2][http], and [sabre/event 2.0.1][evnt]. 2.1.0-alpha2 (2014-10-23) ------------------------- * Added: calendar-user-address-set to default principal search properties list. This should fix iOS attendee autocomplete support. * Changed: Moved all 'notifications' functionality from `Sabre\CalDAV\Plugin` to a new plugin: `Sabre\CalDAV\Notifications\Plugin`. If you want to use notifications-related functionality, just add this plugin. * Changed: Accessing the caldav inbox, outbox or notification collection no longer triggers getCalendarsForUser() on backends. * #533: New invites are no longer delivered to taks-only calendars. * #538: Added `calendarObjectChange` event. * Scheduling speedups. * #539: added `afterResponse` event. (@joserobleda) * Deprecated: All the "tableName" constructor arguments for all the PDO backends are now deprecated. They still work, but will be removed in the next major sabredav version. Every argument that is now deprecated can now be accessed as a public property on the respective backends. * #529: Added getCalendarObjectByUID to PDO backend, speeding up scheduling operations on large calendars. * The zip release ships with [sabre/vobject 3.3.3][vobj], [sabre/http 3.0.2][http], and [sabre/event 2.0.1][evnt]. 2.1.0-alpha1 (2014-09-23) ------------------------- * Added: Support for [rfc6638][rfc6638], also known as CalDAV Scheduling. * Added: Automatically converting between vCard 3, 4 and jCard using the `Accept:` header, in CardDAV reports, and automatically converting from jCard to vCard upon `PUT`. It's important to note that your backends _may_ now recieve both vCard 3.0 and 4.0. * Added: #444. Collections can now opt-in to support high-speed `MOVE`. * Changed: PropertyStorage backends now have a `move` method. * Added: `beforeMove`, and `afterMove` events. * Changed: A few database changes for the CalDAV PDO backend. Make sure you run `bin/migrate21.php` to upgrade your database schema. * Changed: CalDAV backends have a new method: `getCalendarObjectByUID`. This method MUST be implemented by all backends, but the `AbstractBackend` has a simple default implementation for this. * Changed: `Sabre\CalDAV\UserCalendars` has been renamed to `Sabre\CalDAV\CalendarHome`. * Changed: `Sabre\CalDAV\CalendarRootNode` has been renamed to `Sabre\CalDAV\CalendarRoot`. * Changed: The IMipHandler has been completely removed. With CalDAV scheduling support, it is no longer needed. It's functionality has been replaced by `Sabre\CalDAV\Schedule\IMipPlugin`, which can now send emails for clients other than iCal. * Removed: `Sabre\DAV\ObjectTree` and `Sabre\DAV\Tree\FileSystem`. All this functionality has been merged into `Sabre\DAV\Tree`. * Changed: PrincipalBackend now has a findByUri method. * Changed: `PrincipalBackend::searchPrincipals` has a new optional `test` argument. * Added: Support for the `{http://calendarserver.org/ns/}email-address-set` property. * #460: PropertyStorage must move properties during `MOVE` requests. * Changed: Restructured the zip distribution to be a little bit more lean and consistent. * #524: Full support for the `test="anyof"` attribute in principal-search `REPORT`. * #472: Always returning lock tokens in the lockdiscovery property. * Directory entries in the Browser plugin are sorted by type and name. (@aklomp) * #486: It's now possible to return additional properties when an 'allprop' PROPFIND request is being done. (@aklomp) * Changed: Now return HTTP errors when an addressbook-query REPORT is done on a uri that's not a vcard. This should help with debugging this common mistake. * Changed: `PUT` requests with a `Content-Range` header now emit a 400 status instead of 501, as per RFC7231. * Added: Browser plugin can now display the contents of the `{DAV:}supported-privilege-set` property. * Added: Now reporting `CALDAV:max-resource-size`, but we're not actively restricting it yet. * Changed: CalDAV plugin is now responsible for reporting `CALDAV:supported-collation-set` and `CALDAV:supported-calendar-data` properties. * Added: Now reporting `CARDDAV:max-resource-size`, but we're not actively restricting it yet. * Added: Support for `CARDDAV:supported-collation-set`. * Changed: CardDAV plugin is now responsible for reporting `CARDDAV:supported-address-data`. This functionality has been removed from the CardDAV PDO backend. * When a REPORT is not supported, we now emit HTTP error 415, instead of 403. * #348: `HEAD` requests now work wherever `GET` also works. * Changed: Lower priority for the iMip plugins `schedule` event listener. * Added: #523 Custom CalDAV backends can now mark any calendar as read-only. * The zip release ships with [sabre/vobject 3.3.3][vobj], [sabre/http 3.0.0][http], and [sabre/event 2.0.0][evnt]. 2.0.9 (2015-09-04) ------------------ * #705: A `MOVE` request that gets prevented from deleting the source resource will still remove the target resource. Now all events are triggered before any destructive operations. * The zip release ships with [sabre/vobject 3.4.6][vobj], [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt]. 2.0.8 (2015-07-11) ------------------ * #677: Resources with the name '0' would not get retrieved when using `Depth: infinity` in a `PROPFIND` request. * The zip release ships with [sabre/vobject 3.3.5][vobj], [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt]. 2.0.7 (2015-05-25) ------------------ * #650: Correctly cleaning up change info after deleting calendars (@ErrOrnAmE). * The zip release ships with [sabre/vobject 3.3.4][vobj], [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt]. 2.0.6 (2014-12-10) ------------------ * Added `Sabre\CalDAV\CalendarRoot` as an alias for `Sabre\CalDAV\CalendarRootNode`. The latter is going to be deprecated in 2.1, so this makes it slightly easier to write code that works in both branches. * #497: Making sure we're initializing the sync-token field with a value after migration. * The zip release ships with [sabre/vobject 3.3.4][vobj], [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt]. 2.0.5 (2014-10-14) ------------------ * #514: CalDAV PDO backend didn't work when overriding the 'calendar changes' database table name. * #515: 304 status code was not being sent when checking preconditions. * The zip release ships with [sabre/vobject 3.3.3][vobj], [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt]. 2.0.4 (2014-08-27) ------------------ * #483: typo in calendars creation for PostgreSQL. * #487: Locks are now automatically removed after a node has been deleted. * #496: Improve CalDAV and CardDAV sync when there is no webdav-sync support. * Added: Automatically mapping internal sync-tokens to getctag. * The zip release ships with [sabre/vobject 3.3.1][vobj], [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt]. 2.0.3 (2014-07-14) ------------------ * #474: Fixed PropertyStorage `pathFilter()`. * #476: CSP policy incorrect, causing stylesheets to not load in the browser plugin. * #475: Href properties in the browser plugin sometimes included a backslash. * #478: `TooMuchMatches` exception never worked. This was fixed, and we also took this opportunity to rename it to `TooManyMatches`. * The zip release ships with [sabre/vobject 3.2.4][vobj], [sabre/http 2.0.4][http], and [sabre/event 1.0.1][evnt]. 2.0.2 (2014-06-12) ------------------ * #470: Fixed compatibility with PHP < 5.4.14. * #467: Fixed a problem in `examples/calendarserver.php`. * #466: All the postgresql sample files have been updated. * Fixed: An error would be thrown if a client did a propfind on a node the user didn't have access to. * Removed: Old and broken example code from the `examples/` directory. * The zip release ships with [sabre/vobject 3.2.3][vobj], [sabre/http 2.0.3][http], and [sabre/event 1.0.1][evnt]. 2.0.1 (2014-05-28) ------------------ * #459: PROPFIND requests on Files with no Depth header would return a fatal error. * #464: A PROPFIND allprops request should not return properties with status 404. * The zip release ships with [sabre/vobject 3.2.2][vobj], [sabre/http 2.0.3][http], and [sabre/event 1.0.0][evnt]. 2.0.0 (2014-05-22) ------------------ * The zip release ships with [sabre/vobject 3.2.2][vobj], [sabre/http 2.0.3][http], and [sabre/event 1.0.0][evnt]. * Fixed: #456: Issue in sqlite migration script. * Updated: MySQL database schema optimized by using more efficient column types. * Cleaned up browser design. 2.0.0-beta1 (2014-05-15) ------------------------- * The zip release ships with [sabre/vobject 3.2.2][vobj], [sabre/http 2.0.3][http], and [sabre/event 1.0.0][evnt]. * BC Break: Property updating and fetching got refactored. Read the [migration document][mi20] for more information. This allows for creation of a generic property storage, and other property-related functionality that was not possible before. * BC Break: Removed `propertyUpdate`, `beforeGetProperties` and `afterGetProperties` events. * Fixed: #413: Memory optimizations for the CardDAV PDO backend. * Updated: Brand new browser plugin with more debugging features and a design that is slightly less painful. * Added: Support for the `{DAV:}supported-method-set` property server-wide. * Making it easier for implementors to override how the CardDAV addressbook home is located. * Fixed: Issue #422 Preconditions were not being set on PUT on non-existant files. Not really a chance for data-loss, but incorrect nevertheless. * Fixed: Issue #428: Etag check with `If:` fails if the target is a collection. * Fixed: Issues #430, #431, #433: Locks plugin didn't not properly release filesystem based locks. * Fixed: #443. Support for creating new calendar subscriptions for OS X 10.9.2 and up. * Removed: `Sabre\DAV\Server::NODE_*` constants. * Moved all precondition checking into a central place, instead of having to think about it on a per-method basis. * jCal transformation for calendar-query REPORT now works again. * Switched to PSR-4 * Fixed: #175. Returning ETag header upon a failed `If-Match` or `If-None-Match` check. * Removed: `lib/Sabre/autoload.php`. Use `vendor/autoload.php` instead. * Removed: all the rfc documentation from the sabre/dav source. This made the package needlessly larger. * Updated: Issue #439. Lots of updates in PATCH support. The Sabre_DAV_PartialUpdate_IFile interface is now deprecated and will be removed in a future version. * Added: `Sabre\DAV\Exception\LengthRequired`. 1.9.0-alpha2 (2014-01-14) ------------------------- * The zip release ships with sabre/vobject 3.1.3, sabre/http 2.0.1, and sabre/event 1.0.0. * Added: Browser can now inspect any node, if ?sabreaction=browser is appended. * Fixed: Issue #178. Support for multiple items in the Timeout header. * Fixed: Issue #382. Stricter checking if calendar-query is allowed to run. * Added: Depth: Infinity support for PROPFIND request. Thanks Thomas Müller and Markus Goetz. 1.9.0-alpha1 (2013-11-07) ------------------------- * The zip release ships with sabre/vobject 3.1.3, sabre/http 2.0.0alpha5, and sabre/event 1.0.0. * BC Break: The CardDAV and CalDAV BackendInterface each have a new method: getMultipleCards and getMultipleCalendarObjects. The Abstract and PDO backends have default implementations, but if you implement that interface directly, this method is now required. * BC Break: XML property classes now receive an extra argument in their unserialize method ($propertyMap). This allows for recursively parsing properties, if needed. * BC Break: Now using sabre/event for event emitting/subscription. For plugin authors this means Server::subscribeEvent is now Server::on, and Server::broadcastEvent is now Server::emit. * BC Break: Almost all core functionality moved into a CorePlugin. * BC Break: Most events triggered by the server got an overhaul. * Changed: Sabre\HTTP now moved into a dedicated sabre/http package. * Added: Support for WebDAV-sync (rfc6578). * Added: Support for caldav-subscriptions, which is an easy way for caldav clients to manage a list of subscriptions on the server. * Added: Support for emitting and receiving jCal instead of iCalendar for CalDAV. * Added: BasicCallback authenticaton backend, for creating simple authentication systems without having to define any classes. * Added: A $transactionType property on the server class. This can be used for logging and performance measuring purposes. * Fixed: If event handlers modify the request body from a PUT request, an ETag is no longer sent back. * Added: Sabre\DAV\IMultiGet to optimize requests that retrieve information about lists of resources. * Added: MultiGet support to default CalDAV and CardDAV backends, speeding up the multiget and sync reports quite a bit! * Added: ICSExportPlugin can now generate jCal, filter on time-ranges and expand recurrences. * Fixed: Read-only access to calendars still allows the sharee to modify basic calendar properties, such as the displayname and color. * Changed: The default supportedPrivilegeSet has changed. Most privileges are no longer marked as abstract. * Changed: More elegant ACL management for CalendarObject and Card nodes. * Added: Browser plugin now marks a carddav directory as type Directory, and a shared calendar as 'Shared'. * Added: When debugExceptions is turned on, all previous exceptions are also traversed. * Removed: Got rid of the Version classes for CalDAV, CardDAV, HTTP, and DAVACL. Now that there's no separate packages anymore, this makes a bit more sense. * Added: Generalized the multistatus response parser a bit more, for better re-use. * Added: Sabre\DAV\Client now has support for complex properties for PROPPATCH. (Issue #299). * Added: Sabre\DAV\Client has support for gzip and deflate encoding. * Added: Sabre\DAV\Client now has support for sending objects as streams. * Added: Deserializer for {DAV:}current-user-privilege-set. * Added: Addressbooks or backends can now specify custom acl rules when creating cards. * Added: The ability for plugins to validate custom tokens in If: headers. * Changed: Completely refactored the Lock plugin to deal with the new If: header system. * Added: Checking preconditions for MOVE, COPY, DELETE and PROPPATCH methods. * Added: has() method on DAV\Property\SupportedReportSet. * Added: If header now gets checked (with ETag) all the time. Before the dealing with the If-header was a responsibility of the Locking plugin. * Fixed: Outbox access for delegates. * Added: Issue 333: It's now possible to override the calendar-home in the CalDAV plugin. * Added: A negotiateContentType to HTTP\Request. A convenience method. * Fixed: Issue 349: Denying copying or moving a resource into it's own subtree. * Fixed: SabreDAV catches every exception again. * Added: Issue #358, adding a component=vevent parameter to the content-types for calendar objects, if the caldav backend provides this info. 1.8.12-stable (2015-01-21) -------------------------- * The zip release ships with sabre/vobject 2.1.7. * #568: Support empty usernames and passwords in basic auth. 1.8.11 (2014-12-10) ------------------- * The zip release ships with sabre/vobject 2.1.6. * Updated: MySQL database schema optimized by using more efficient column types. * #516: The DAV client will now only redirect to HTTP and HTTPS urls. 1.8.10 (2014-05-15) ------------------- * The zip release ships with sabre/vobject 2.1.4. * includes changes from version 1.7.12. 1.8.9 (2014-02-26) ------------------ * The zip release ships with sabre/vobject 2.1.3. * includes changes from version 1.7.11. 1.8.8 (2014-02-09) ------------------ * includes changes from version 1.7.10. * The zip release ships with sabre/vobject 2.1.3. 1.8.7 (2013-10-02) ------------------ * the zip release ships with sabre/vobject 2.1.3. * includes changes from version 1.7.9. 1.8.6 (2013-06-18) ------------------ * The zip release ships with sabre/vobject 2.1.0. * Includes changes from version 1.7.8. 1.8.5 (2013-04-11) ------------------ * The zip release ships with sabre/vobject 2.0.7. * Includes changes from version 1.7.7. 1.8.4 (2013-04-08) ------------------ * The zip release ships with sabre/vobject 2.0.7. * Includes changes from version 1.7.6. 1.8.3 (2013-03-01) ------------------ * The zip release ships with sabre/vobject 2.0.6. * Includes changes from version 1.7.5. * Fixed: organizer email-address for shared calendars is now prefixed with mailto:, as it should. 1.8.2 (2013-01-19) ------------------ * The zip release ships with sabre/vobject 2.0.5. * Includes changes from version 1.7.4. 1.8.1 (2012-12-01) ------------------ * The zip release ships with sabre/vobject 2.0.5. * Includes changes from version 1.7.3. * Fixed: Typo in 1.7 migration script caused it to fail. 1.8.0 (2012-11-08) ------------------ * The zip release ships with sabre/vobject 2.0.5. * BC Break: Moved the entire codebase to PHP namespaces. * BC Break: Every backend package (CalDAV, CardDAV, Auth, Locks, Principals) now has consistent naming conventions. There's a BackendInterface, and an AbstractBackend class. * BC Break: Changed a bunch of constructor signatures in the CalDAV package, to reduce dependencies on the ACL package. * BC Break: Sabre_CalDAV_ISharedCalendar now also has a getShares method, so sharees can figure out who is also on a shared calendar. * Added: Sabre_DAVACL_IPrincipalCollection interface, to advertise support for principal-property-search on any node. * Added: Simple console script to fire up a fileserver in the current directory using PHP 5.4's built-in webserver. * Added: Sharee's can now also read out the list of invites for a shared calendar. * Added: The Proxy principal classes now both implement an interface, for greater flexiblity. 1.7.13 (2014-07-28) ------------------- * The zip release ships with sabre/vobject 2.1.4. * Changed: Removed phing and went with a custom build script for now. 1.7.12 (2014-05-15) ------------------- * The zip release ships with sabre/vobject 2.1.4. * Updated: Issue #439. Lots of updates in PATCH support. The Sabre_DAV_PartialUpdate_IFile interface is now deprecated and will be removed in a future version. * Fixed: Restoring old setting after changing libxml_disable_entity_loader. * Fixed: Issue #422: Preconditions were not being set on PUT on non-existant files. Not really a chance for data-loss, but incorrect nevertheless. * Fixed: Issue #427: Now checking preconditions on DELETE requests. * Fixed: Issue #428: Etag check with If: fails if the target is a collection. * Fixed: Issue #393: PATCH request with missing end-range was handled incorrectly. * Added: Sabre_DAV_Exception_LengthRequired to omit 411 errors. 1.7.11 (2014-02-26) ------------------- * The zip release ships with sabre/vobject 2.1.3. * Fixed: Issue #407: large downloads failed. * Fixed: Issue #414: XXE security problem on older PHP versions. 1.7.10 (2014-02-09) ------------------- * Fixed: Issue #374: Don't urlescape colon (:) when it's not required. * Fixed: Potential security vulnerability in the http client. 1.7.9 (2013-10-02) ------------------ * The zip release ships with sabre/vobject 2.1.3. * Fixed: Issue #365. Incorrect output when principal urls have spaces in them. * Added: Issue #367: Automatically adding a UID to vcards that don't have them. 1.7.8 (2013-06-17) ------------------ * The zip release ships with sabre/vobject 2.1.0. * Changed: Sabre\DAV\Client::verifyPeer is now a protected property (instead of private). * Fixed: Text was incorrectly escaped in the Href and HrefList properties, disallowing urls with ampersands (&) in them. * Added: deserializer for Sabre\DAVACL\Property\CurrentUserPrivilegeSet. * Fixed: Issue 335: Client only deserializes properties with status 200. * Fixed: Issue 341: Escaping xml in 423 Locked error responses. * Added: Issue 339: beforeGetPropertiesForPath event. 1.7.7 (2013-04-11) ------------------ * The zip release ships with sabre/vobject 2.0.7. * Fixed: Assets in the browser plugins were not being served on windows machines. 1.7.6 (2013-04-08) ------------------ * The zip release ships with sabre/vobject 2.0.7. * Fixed: vcardurl in database schema can now hold 255 characters instead of 80 (which is often way to small). * Fixed: The browser plugin potentially allowed people to open any arbitrary file on windows servers (CVE-2013-1939). 1.7.5 (2013-03-01) ------------------ * The zip release ships with sabre/vobject 2.0.6. * Change: No longer advertising support for 4.0 vcards. iOS and OS X address book don't handle this well, and just advertising 3.0 support seems like the most logical course of action. * Added: ->setVerifyPeers to Sabre_DAV_Client (greatly resisting against it, don't use this..). 1.7.4 (2013-01-19) ------------------ * The zip release ships with sabre/vobject 2.0.5. * Changed: To be compatibile with MS Office 2011 for Mac, a workaround was removed that was added to support old versions of Windows XP (pre-SP3). Indeed! We needed a crazy workaround to work with one MS product in the past, and we can't keep that workaround to be compatible with another MS product. * Fixed: expand-properties REPORT had incorrect values for the href element. * Fixed: Range requests now work for non-seekable streams. (Thanks Alfred Klomp). * Fixed: Changed serialization of {DAV:}getlastmodified and {DAV:}supportedlock to improve compatiblity with MS Office 2011 for Mac. * Changed: reverted the automatic translation of 'DAV:' xml namespaces to 'urn:DAV' when parsing files. Issues were reported with libxml 2.6.32, on a relatively recent debian release, so we'll wait till 2015 to take this one out again. * Added: Sabre_DAV_Exception_ServiceUnavailable, for emitting 503's. 1.7.3 (2012-12-01) ------------------ * The zip release ships with sabre/vobject 2.0.5. * Fixed: Removing double slashes from getPropertiesForPath. * Change: Marked a few more properties in the CardDAV as protected, instead of private. * Fixed: SharingPlugin now plays nicer with other plugins with similar functionality. * Fixed: Issue 174. Sending back HTTP/1.0 for requests with this version. 1.7.2 (2012-11-08) ------------------ * The zip release ships with sabre/vobject 2.0.5. * Added: ACL plugin advertises support for 'calendarserver-principal- property-search'. * Fixed: [#153] Allowing for relative http principals in iMip requests. * Added: Support for cs:first-name and cs:last-name properties in sharing invites. * Fixed: Made a bunch of properties protected, where they were private before. * Added: Some non-standard properties for sharing to improve compatibility. * Fixed: some bugfixes in postgres sql script. * Fixed: When requesting some properties using PROPFIND, they could show up as both '200 Ok' and '403 Forbidden'. * Fixed: calendar-proxy principals were not checked for deeper principal membership than 1 level. * Fixed: setGroupMemberSet argument now correctly receives relative principal urls, instead of the absolute ones. * Fixed: Server class will filter out any bonus properties if any extra were returned. This means the implementor of the IProperty class can be a bit lazier when implementing. Note: bug numbers after this line refer to Google Code tickets. We're using github now. 1.7.1 (2012-10-07) ------------------ * Fixed: include path problem in the migration script. 1.7.0 (2012-10-06) ------------------ * 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) * 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. * BC Break: The DAV: namespace is no longer converted to urn:DAV. This was a workaround for a bug in older PHP versions (pre-5.3). * Removed: Sabre.includes.php was deprecated, and is now removed. * Removed: Sabre_CalDAV_Server was deprecated, and is now removed. Please use Sabre_DAV_Server and check the examples in the examples/ directory. * Changed: The Sabre_VObject library now spawned into it's own project! The VObject library is still included in the SabreDAV zip package. * Added: Experimental interfaces to allow implementation of caldav-sharing. Note that no implementation is provided yet, just the api hooks. * Added: Free-busy reporting compliant with the caldav-scheduling standard. This allows iCal and other clients to fetch other users' free-busy data. * Added: Experimental NotificationSupport interface to add caldav notifications. * Added: VCF Export plugin. If enabled, it can generate an export of an entire addressbook. * Added: Support for PATCH using a SabreDAV format, to live-patch files. * Added: Support for Prefer: return-minimal and Brief: t headers for PROPFIND and PROPPATCH requests. * 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. * Changed: We are now using the composer autoloader. * Changed: The CalDAV backend now all implement an interface. * Changed: Instead of Sabre_DAV_Property, Sabre_DAV_PropertyInterface is now the basis of every property class. * Update: Caching results for principal lookups. This should cut down queries and performance for a number of heavy requests. * Update: ObjectTree caches lookups much more aggresively, which will help especially speeding up a bunch of REPORT queries. * Added: Support for the schedule-calendar-transp property. * 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. * Added: Tons of timezone name mappings for Microsoft Exchange. * Added: Support for an 'exception' event in the server class. * Fixed: Uploaded VCards without a UID are now rejected. (thanks Dominik!) * Fixed: Rejecting calendar objects if they are not in the supported-calendar-component list. (thanks Armin!) * Fixed: Issue 219: serialize() now reorders correctly. * Fixed: Sabre_DAV_XMLUtil no longer returns empty $dom->childNodes if there is whitespace in $dom. * Fixed: Returning 409 Conflict instead of 500 when an attempt is made to create a file as a child of something that's not a collection. * Fixed: Issue 237: xml-encoding values in SabreDAV error responses. * Fixed: Returning 403, instead of 501 when an unknown REPORT is requested. * Fixed: Postfixing slash on {DAV:}owner properties. * Fixed: Several embarrassing spelling mistakes in docblocks. 1.6.10 (2013-06-17) ------------------- * Fixed: Text was incorrectly escaped in the Href and HrefList properties, disallowing urls with ampersands (&) in them. * Fixed: Issue 341: Escaping xml in 423 Locked error responses. 1.6.9 (2013-04-11) ------------------ * Fixed: Assets in the browser plugins were not being served on windows machines. 1.6.8 (2013-04-08) ------------------ * Fixed: vcardurl in database schema can now hold 255 characters instead of 80 (which is often way to small). * Fixed: The browser plugin potentially allowed people to open any arbitrary file on windows servers. (CVE-2013-1939). 1.6.7 (2013-03-01) ------------------ * Change: No longer advertising support for 4.0 vcards. iOS and OS X address book don't handle this well, and just advertising 3.0 support seems like the most logical course of action. * Added: ->setVerifyPeers to Sabre_DAV_Client (greatly resisting against it, don't use this..). 1.6.6 (2013-01-19) ------------------ * Fixed: Backported a fix for broken XML serialization in error responses. (Thanks @DeepDiver1975!) 1.6.5 (2012-10-04) ------------------ * Fixed: Workaround for line-ending bug OS X 10.8 addressbook has. * Added: Ability to allow users to set SSL certificates for the Client class. (Thanks schiesbn!). * Fixed: Directory indexes with lots of nodes should be a lot faster. * Fixed: Issue 235: E_NOTICE thrown when doing a propfind request with Sabre_DAV_Client, and no valid properties are returned. * Fixed: Issue with filtering on alarms in tasks. 1.6.4 (2012-08-02) ------------------ * Fixed: Issue 220: Calendar-query filters may fail when filtering on alarms, if an overridden event has it's alarm removed. * Fixed: Compatibility for OS/X 10.8 iCal in the IMipHandler. * Fixed: Issue 222: beforeWriteContent shouldn't be called for lock requests. * Fixed: Problem with POST requests to the outbox if mailto: was not lower cased. * Fixed: Yearly recurrence rule expansion on leap-days no behaves correctly. * Fixed: Correctly checking if recurring, all-day events with no dtstart fall in a timerange if the start of the time-range exceeds the start of the instance of an event, but not the end. * Fixed: All-day recurring events wouldn't match if an occurence ended exactly on the start of a time-range. * Fixed: HTTP basic auth did not correctly deal with passwords containing colons on some servers. * Fixed: Issue 228: DTEND is now non-inclusive for all-day events in the calendar-query REPORT and free-busy calculations. 1.6.3 (2012-06-12) ------------------ * 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 * Fixed: Issue 217: Sabre_DAV_Tree_FileSystem was pretty broken. 1.6.2 (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 (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 (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 (2012-04-16) ------------------ * Fixed: Issue with parsing timezone identifiers that were surrounded by quotes. (Fixes emClient compatibility). 1.5.8 (2012-02-22) ------------------ * Fixed: Issue 95: Another timezone parsing issue, this time in calendar-query. 1.5.7 (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 (2012-01-07) ------------------ * Fixed: Issue 174: VObject could break UTF-8 characters. * Fixed: pear package installation issues. 1.5.5 (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 (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 (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 (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 (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 (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 (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 (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.5 (2010-08-18) ------------------ * Fixed: Issue 73: guessBaseUrl fails for some servers. * Fixed: Issue 67: SabreDAV works better with non-seekable streams. * Fixed: If-Modified-Since and If-Unmodified-Since would be incorrect if the date is an exact match. 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 (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 (2010-04-15) ------------------- * Fixed: double namespace declaration in multistatus responses. 1.0.13 (2010-03-30) ------------------- * Fixed: Issue 40: Last references to basename/dirname 1.0.12 (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 (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 (2010-03-22) ------------------- * Fixed: Issue 34: Invalid Lock-Token header response. * Added: Issue 35: Addign SabreDAV version to HTTP OPTIONS responses. 1.0.9 (2010-03-19) ------------------ * Fixed: Issue 27: Entities not being encoded in PROPFIND responses. * Fixed: Issue 29: Added missing TIMEOUT_INFINITE constant. 1.0.8 (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 (2010-02-24) ------------------ * Fixed: Issue 19 regression for MS Office 1.0.6 (2010-02-23) ------------------ * Fixed: Issue 19: HEAD requests on Collections 1.0.5 (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 (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 (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 (2009-12-22) ------------------ * Fixed: Issue 15: typos in examples * Fixed: Minor pear installation issues 1.0.0 (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.9a-alpha (2009-07-21) ---------------------- * Fixed: Broken release 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 [vobj]: http://sabre.io/vobject/ [evnt]: http://sabre.io/event/ [http]: http://sabre.io/http/ [mi20]: http://sabre.io/dav/upgrade/1.8-to-2.0/ [rfc6638]: http://tools.ietf.org/html/rfc6638 "CalDAV Scheduling" sabre-dav-2.1.10/LICENSE000066400000000000000000000030501267035675400144610ustar00rootroot00000000000000Copyright (C) 2007-2016 fruux GmbH (https://fruux.com/). 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 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. sabre-dav-2.1.10/README.md000066400000000000000000000023241267035675400147360ustar00rootroot00000000000000SabreDAV ======== Introduction ------------ SabreDAV is the most popular WebDAV framework for PHP. Use it to create WebDAV, CalDAV and CardDAV servers. Full documentation can be found on the website: http://sabre.io/ Build status ------------ | branch | status | | ----------- | ------ | | master | [![Build Status](https://travis-ci.org/fruux/sabre-dav.png?branch=master)](https://travis-ci.org/fruux/sabre-dav) | | 2.0 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.png?branch=2.0)](https://travis-ci.org/fruux/sabre-dav) | | 1.8 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.png?branch=1.8)](https://travis-ci.org/fruux/sabre-dav) | | 1.7 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.png?branch=1.7)](https://travis-ci.org/fruux/sabre-dav) | | 1.6 | [![Build Status](https://travis-ci.org/fruux/sabre-dav.png?branch=1.6)](https://travis-ci.org/fruux/sabre-dav) | | xml-rewrite | [![Build Status](https://travis-ci.org/fruux/sabre-dav.png?branch=xml-rewrite)](https://travis-ci.org/fruux/sabre-dav) | Made at fruux ------------- SabreDAV is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. sabre-dav-2.1.10/bin/000077500000000000000000000000001267035675400142265ustar00rootroot00000000000000sabre-dav-2.1.10/bin/build.php000066400000000000000000000074351267035675400160470ustar00rootroot00000000000000 [ 'init', 'test', 'clean', ], 'markrelease' => [ 'init', 'test', 'clean', ], 'clean' => [], 'test' => [ 'composerupdate', ], 'init' => [], 'composerupdate' => [], ]; $default = 'buildzip'; $baseDir = __DIR__ . '/../'; chdir($baseDir); $currentTask = $default; if ($argc > 1) $currentTask = $argv[1]; $version = null; if ($argc > 2) $version = $argv[2]; if (!isset($tasks[$currentTask])) { echo "Task not found: ", $currentTask, "\n"; die(1); } // Creating the dependency graph $newTaskList = []; $oldTaskList = [$currentTask => true]; while(count($oldTaskList)>0) { foreach($oldTaskList as $task=>$foo) { if (!isset($tasks[$task])) { echo "Dependency not found: " . $task, "\n"; die(1); } $dependencies = $tasks[$task]; $fullFilled = true; foreach($dependencies as $dependency) { if (isset($newTaskList[$dependency])) { // Already in the fulfilled task list. continue; } else { $oldTaskList[$dependency] = true; $fullFilled = false; } } if ($fullFilled) { unset($oldTaskList[$task]); $newTaskList[$task] = 1; } } } foreach(array_keys($newTaskList) as $task) { echo "task: " . $task, "\n"; call_user_func($task); echo "\n"; } function init() { global $version; if (!$version) { include __DIR__ . '/../vendor/autoload.php'; $version = Sabre\DAV\Version::VERSION; } echo " Building sabre/dav " . $version, "\n"; } function clean() { global $baseDir; echo " Removing build files\n"; $outputDir = $baseDir . '/build/SabreDAV'; if (is_dir($outputDir)) { system('rm -r ' . $baseDir . '/build/SabreDAV'); } } function composerupdate() { global $baseDir; echo " Updating composer packages to latest version\n\n"; system('cd ' . $baseDir . '; composer update'); } function test() { global $baseDir; echo " Running all unittests.\n"; echo " This may take a while.\n\n"; system(__DIR__ . '/phpunit --configuration ' . $baseDir . '/tests/phpunit.xml --stop-on-failure', $code); if ($code != 0) { echo "PHPUnit reported error code $code\n"; die(1); } } function buildzip() { global $baseDir, $version; echo " Generating composer.json\n"; $input = json_decode(file_get_contents(__DIR__ . '/../composer.json'), true); $newComposer = [ "require" => $input['require'], "config" => [ "bin-dir" => "./bin", ] ]; $newComposer['require']['sabre/dav'] = $version; mkdir('build/SabreDAV'); file_put_contents('build/SabreDAV/composer.json', json_encode($newComposer)); echo " Downloading dependencies\n"; system("cd build/SabreDAV; composer install -n --no-dev", $code); if ($code!==0) { echo "Composer reported error code $code\n"; die(1); } echo " Removing pointless files\n"; unlink('build/SabreDAV/composer.json'); unlink('build/SabreDAV/composer.lock'); echo " Moving important files to the root of the project\n"; $fileNames = [ 'ChangeLog.md', 'LICENSE', 'README.md', 'examples', ]; foreach($fileNames as $fileName) { echo " $fileName\n"; rename('build/SabreDAV/vendor/sabre/dav/' . $fileName, 'build/SabreDAV/' . $fileName); } // echo "\n"; echo "Zipping the sabredav distribution\n\n"; system('cd build; zip -qr sabredav-' . $version . '.zip SabreDAV'); echo "Done."; } sabre-dav-2.1.10/bin/googlecode_upload.py000077500000000000000000000213211267035675400202550ustar00rootroot00000000000000#!/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()) sabre-dav-2.1.10/bin/migrateto17.php000077500000000000000000000203311267035675400171040ustar00rootroot00000000000000#!/usr/bin/env php 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"; switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) { case 'mysql' : $pdo->exec(<<exec('ALTER TABLE calendarobjects ADD etag text'); $pdo->exec('ALTER TABLE calendarobjects ADD size integer'); $pdo->exec('ALTER TABLE calendarobjects ADD componenttype TEXT'); $pdo->exec('ALTER TABLE calendarobjects ADD firstoccurence integer'); $pdo->exec('ALTER TABLE calendarobjects ADD lastoccurence integer'); break; default : die('This upgrade script does not support this driver (' . $pdo->getAttribute(PDO::ATTR_DRIVER_NAME) . ")\n"); } 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 "Completed: $done / $total\n"; echo "Checking the calendars table needs changes.\n"; $row = $pdo->query("SELECT * FROM calendars LIMIT 1")->fetch(); if (array_key_exists('transparent', $row)) { echo "The calendars table is already up to date\n"; } else { echo "Adding the 'transparent' field to the calendars table\n"; switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) { case 'mysql' : $pdo->exec("ALTER TABLE calendars ADD transparent TINYINT(1) NOT NULL DEFAULT '0'"); break; case 'sqlite' : $pdo->exec("ALTER TABLE calendars ADD transparent bool"); break; default : die('This upgrade script does not support this driver (' . $pdo->getAttribute(PDO::ATTR_DRIVER_NAME) . ")\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->hasTime()) { $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(\Sabre\CalDAV\Backend\PDO::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, ); } sabre-dav-2.1.10/bin/migrateto20.php000077500000000000000000000304041267035675400171000ustar00rootroot00000000000000#!/usr/bin/env php setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); switch($driver) { case 'mysql' : echo "Detected MySQL.\n"; break; case 'sqlite' : echo "Detected SQLite.\n"; break; default : echo "Error: unsupported driver: " . $driver . "\n"; die(-1); } foreach(['calendar', 'addressbook'] as $itemType) { $tableName = $itemType . 's'; $tableNameOld = $tableName . '_old'; $changesTable = $itemType . 'changes'; echo "Upgrading '$tableName'\n"; // The only cross-db way to do this, is to just fetch a single record. $row = $pdo->query("SELECT * FROM $tableName LIMIT 1")->fetch(); if (!$row) { echo "No records were found in the '$tableName' table.\n"; echo "\n"; echo "We're going to rename the old table to $tableNameOld (just in case).\n"; echo "and re-create the new table.\n"; switch($driver) { case 'mysql' : $pdo->exec("RENAME TABLE $tableName TO $tableNameOld"); switch($itemType) { case 'calendar' : $pdo->exec(" CREATE TABLE calendars ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, principaluri VARCHAR(100), displayname VARCHAR(100), uri VARCHAR(200), synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1', description TEXT, calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0', calendarcolor VARCHAR(10), timezone TEXT, components VARCHAR(20), transparent TINYINT(1) NOT NULL DEFAULT '0', UNIQUE(principaluri, uri) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; "); break; case 'addressbook' : $pdo->exec(" CREATE TABLE addressbooks ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, principaluri VARCHAR(255), displayname VARCHAR(255), uri VARCHAR(200), description TEXT, synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1', UNIQUE(principaluri, uri) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; "); break; } break; case 'sqlite' : $pdo->exec("ALTER TABLE $tableName RENAME TO $tableNameOld"); switch($itemType) { case 'calendar' : $pdo->exec(" CREATE TABLE calendars ( id integer primary key asc, principaluri text, displayname text, uri text, synctoken integer, description text, calendarorder integer, calendarcolor text, timezone text, components text, transparent bool ); "); break; case 'addressbook' : $pdo->exec(" CREATE TABLE addressbooks ( id integer primary key asc, principaluri text, displayname text, uri text, description text, synctoken integer ); "); break; } break; } echo "Creation of 2.0 $tableName table is complete\n"; } else { // Checking if there's a synctoken field already. if (array_key_exists('synctoken', $row)) { echo "The 'synctoken' field already exists in the $tableName table.\n"; echo "It's likely you already upgraded, so we're simply leaving\n"; echo "the $tableName table alone\n"; } else { echo "1.8 table schema detected\n"; switch($driver) { case 'mysql' : $pdo->exec("ALTER TABLE $tableName ADD synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1'"); $pdo->exec("ALTER TABLE $tableName DROP ctag"); $pdo->exec("UPDATE $tableName SET synctoken = '1'"); break; case 'sqlite' : $pdo->exec("ALTER TABLE $tableName ADD synctoken integer"); $pdo->exec("UPDATE $tableName SET synctoken = '1'"); echo "Note: there's no easy way to remove fields in sqlite.\n"; echo "The ctag field is no longer used, but it's kept in place\n"; break; } echo "Upgraded '$tableName' to 2.0 schema.\n"; } } try { $pdo->query("SELECT * FROM $changesTable LIMIT 1"); echo "'$changesTable' already exists. Assuming that this part of the\n"; echo "upgrade was already completed.\n"; } catch (Exception $e) { echo "Creating '$changesTable' table.\n"; switch($driver) { case 'mysql' : $pdo->exec(" CREATE TABLE $changesTable ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARCHAR(200) NOT NULL, synctoken INT(11) UNSIGNED NOT NULL, {$itemType}id INT(11) UNSIGNED NOT NULL, operation TINYINT(1) NOT NULL, INDEX {$itemType}id_synctoken ({$itemType}id, synctoken) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; "); break; case 'sqlite' : $pdo->exec(" CREATE TABLE $changesTable ( id integer primary key asc, uri text, synctoken integer, {$itemType}id integer, operation bool ); "); $pdo->exec("CREATE INDEX {$itemType}id_synctoken ON $changesTable ({$itemType}id, synctoken);"); break; } } } try { $pdo->query("SELECT * FROM calendarsubscriptions LIMIT 1"); echo "'calendarsubscriptions' already exists. Assuming that this part of the\n"; echo "upgrade was already completed.\n"; } catch (Exception $e) { echo "Creating calendarsubscriptions table.\n"; switch($driver) { case 'mysql' : $pdo->exec(" CREATE TABLE calendarsubscriptions ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARCHAR(200) NOT NULL, principaluri VARCHAR(100) NOT NULL, source TEXT, displayname VARCHAR(100), refreshrate VARCHAR(10), calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0', calendarcolor VARCHAR(10), striptodos TINYINT(1) NULL, stripalarms TINYINT(1) NULL, stripattachments TINYINT(1) NULL, lastmodified INT(11) UNSIGNED, UNIQUE(principaluri, uri) ); "); break; case 'sqlite' : $pdo->exec(" CREATE TABLE calendarsubscriptions ( id integer primary key asc, uri text, principaluri text, source text, displayname text, refreshrate text, calendarorder integer, calendarcolor text, striptodos bool, stripalarms bool, stripattachments bool, lastmodified int ); "); $pdo->exec("CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);"); break; } } try { $pdo->query("SELECT * FROM propertystorage LIMIT 1"); echo "'propertystorage' already exists. Assuming that this part of the\n"; echo "upgrade was already completed.\n"; } catch (Exception $e) { echo "Creating propertystorage table.\n"; switch($driver) { case 'mysql' : $pdo->exec(" CREATE TABLE propertystorage ( id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, path VARBINARY(1024) NOT NULL, name VARBINARY(100) NOT NULL, value MEDIUMBLOB ); "); $pdo->exec(" CREATE UNIQUE INDEX path_property ON propertystorage (path(600), name(100)); "); break; case 'sqlite' : $pdo->exec(" CREATE TABLE propertystorage ( id integer primary key asc, path TEXT, name TEXT, value TEXT ); "); $pdo->exec(" CREATE UNIQUE INDEX path_property ON propertystorage (path, name); "); break; } } echo "Upgrading cards table to 2.0 schema\n"; try { $create = false; $row = $pdo->query("SELECT * FROM cards LIMIT 1")->fetch(); if (!$row) { echo "There was no data in the cards table, so we're re-creating it\n"; echo "The old table will be renamed to cards_old, just in case.\n"; $create = true; switch($driver) { case 'mysql' : $pdo->exec("RENAME TABLE cards TO cards_old"); break; case 'sqlite' : $pdo->exec("ALTER TABLE cards RENAME TO cards_old"); break; } } } catch (Exception $e) { echo "Exception while checking cards table. Assuming that the table does not yet exist.\n"; $create = true; } if ($create) { switch($driver) { case 'mysql' : $pdo->exec(" 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, etag VARBINARY(32), size INT(11) UNSIGNED NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; "); break; case 'sqlite' : $pdo->exec(" CREATE TABLE cards ( id integer primary key asc, addressbookid integer, carddata blob, uri text, lastmodified integer etag text, size integer ); "); break; } } else { switch($driver) { case 'mysql' : $pdo->exec(" ALTER TABLE cards ADD etag VARBINARY(32), ADD size INT(11) UNSIGNED NOT NULL; "); break; case 'sqlite' : $pdo->exec(" ALTER TABLE cards ADD etag text; ALTER TABLE cards ADD size integer; "); break; } echo "Reading all old vcards and populating etag and size fields.\n"; $result = $pdo->query('SELECT id, carddata FROM cards'); $stmt = $pdo->prepare('UPDATE cards SET etag = ?, size = ? WHERE id = ?'); while($row = $result->fetch(\PDO::FETCH_ASSOC)) { $stmt->execute([ md5($row['carddata']), strlen($row['carddata']), $row['id'] ]); } } echo "Upgrade to 2.0 schema completed.\n"; sabre-dav-2.1.10/bin/migrateto21.php000077500000000000000000000106751267035675400171110ustar00rootroot00000000000000#!/usr/bin/env php setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); switch($driver) { case 'mysql' : echo "Detected MySQL.\n"; break; case 'sqlite' : echo "Detected SQLite.\n"; break; default : echo "Error: unsupported driver: " . $driver . "\n"; die(-1); } echo "Upgrading 'calendarobjects'\n"; $addUid = false; try { $result = $pdo->query('SELECT * FROM calendarobjects LIMIT 1'); $row = $result->fetch(\PDO::FETCH_ASSOC); if (!$row) { echo "No data in table. Going to try to add the uid field anyway.\n"; $addUid = true; } elseif (array_key_exists('uid', $row)) { echo "uid field exists. Assuming that this part of the migration has\n"; echo "Already been completed.\n"; } else { echo "2.0 schema detected.\n"; $addUid = true; } } catch (Exception $e) { echo "Could not find a calendarobjects table. Skipping this part of the\n"; echo "upgrade.\n"; } if ($addUid) { switch($driver) { case 'mysql' : $pdo->exec('ALTER TABLE calendarobjects ADD uid VARCHAR(200)'); break; case 'sqlite' : $pdo->exec('ALTER TABLE calendarobjects ADD uid TEXT'); break; } $result = $pdo->query('SELECT id, calendardata FROM calendarobjects'); $stmt = $pdo->prepare('UPDATE calendarobjects SET uid = ? WHERE id = ?'); $counter = 0; while($row = $result->fetch(\PDO::FETCH_ASSOC)) { try { $vobj = \Sabre\VObject\Reader::read($row['calendardata']); } catch (\Exception $e) { echo "Warning! Item with id $row[id] could not be parsed!\n"; continue; } $uid = null; $item = $vobj->getBaseComponent(); if (!isset($item->UID)) { echo "Warning! Item with id $item[id] does NOT have a UID property and this is required.\n"; continue; } $uid = (string)$item->UID; $stmt->execute([$uid, $row['id']]); $counter++; } } echo "Creating 'schedulingobjects'\n"; switch($driver) { case 'mysql' : $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, principaluri VARCHAR(255), calendardata MEDIUMBLOB, uri VARCHAR(200), lastmodified INT(11) UNSIGNED, etag VARCHAR(32), size INT(11) UNSIGNED NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; '); break; case 'sqlite' : $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects ( id integer primary key asc, principaluri text, calendardata blob, uri text, lastmodified integer, etag text, size integer ) '); break; $pdo->exec(' CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri); '); break; } echo "Done.\n"; echo "Upgrade to 2.1 schema completed.\n"; sabre-dav-2.1.10/bin/naturalselection000077500000000000000000000076621267035675400175430ustar00rootroot00000000000000#!/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() sabre-dav-2.1.10/bin/sabredav000077500000000000000000000000701267035675400157400ustar00rootroot00000000000000#!/bin/sh php -S 0.0.0.0:8080 `dirname $0`/sabredav.php sabre-dav-2.1.10/bin/sabredav.php000077500000000000000000000014501267035675400165310ustar00rootroot00000000000000stream = fopen('php://stdout','w'); } function log($msg) { fwrite($this->stream, $msg . "\n"); } } $log = new CliLog(); if (php_sapi_name()!=='cli-server') { die("This script is intended to run on the built-in php webserver"); } // Finding composer $paths = array( __DIR__ . '/../vendor/autoload.php', __DIR__ . '/../../../autoload.php', ); foreach($paths as $path) { if (file_exists($path)) { include $path; break; } } use Sabre\DAV; // Root $root = new DAV\FS\Directory(getcwd()); // Setting up server. $server = new DAV\Server($root); // Browser plugin $server->addPlugin(new DAV\Browser\Plugin()); $server->exec(); sabre-dav-2.1.10/composer.json000066400000000000000000000030531267035675400162010ustar00rootroot00000000000000{ "name": "sabre/dav", "type": "library", "description": "WebDAV Framework for PHP", "keywords": ["Framework", "WebDAV", "CalDAV", "CardDAV", "iCalendar"], "homepage": "http://sabre.io/", "license" : "BSD-3-Clause", "authors": [ { "name": "Evert Pot", "email": "me@evertpot.com", "homepage" : "http://evertpot.com/", "role" : "Developer" } ], "require": { "php": ">=5.4.1", "sabre/vobject": "^3.3.4", "sabre/event" : "^2.0.0", "sabre/http" : "^3.0.0", "ext-dom": "*", "ext-pcre": "*", "ext-spl": "*", "ext-simplexml": "*", "ext-mbstring" : "*", "ext-ctype" : "*", "ext-date" : "*", "ext-iconv" : "*", "ext-libxml" : "*" }, "require-dev" : { "phpunit/phpunit" : "~4.2", "evert/phpdoc-md" : "~0.1.0", "squizlabs/php_codesniffer": "~1.5.3" }, "suggest" : { "ext-curl" : "*", "ext-pdo" : "*" }, "autoload": { "psr-4" : { "Sabre\\DAV\\" : "lib/DAV/", "Sabre\\DAVACL\\" : "lib/DAVACL/", "Sabre\\CalDAV\\" : "lib/CalDAV/", "Sabre\\CardDAV\\" : "lib/CardDAV/" } }, "support" : { "forum" : "https://groups.google.com/group/sabredav-discuss", "source" : "https://github.com/fruux/sabre-dav" }, "bin" : [ "bin/sabredav", "bin/naturalselection" ], "config" : { "bin-dir" : "./bin" } } sabre-dav-2.1.10/examples/000077500000000000000000000000001267035675400152745ustar00rootroot00000000000000sabre-dav-2.1.10/examples/addressbookserver.php000066400000000000000000000033071267035675400215370ustar00rootroot00000000000000setAttribute(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 'vendor/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 = [ new Sabre\DAVACL\PrincipalCollection($principalBackend), // new Sabre\CalDAV\CalendarRoot($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()); $server->addPlugin(new Sabre\DAV\Sync\Plugin()); // And off we go! $server->exec(); sabre-dav-2.1.10/examples/calendarserver.php000066400000000000000000000034721267035675400210130ustar00rootroot00000000000000setAttribute(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 'vendor/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 = [ new Sabre\CalDAV\Principal\Collection($principalBackend), new Sabre\CalDAV\CalendarRoot($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); /* CalDAV support */ $caldavPlugin = new Sabre\CalDAV\Plugin(); $server->addPlugin($caldavPlugin); /* Calendar subscription support */ $server->addPlugin( new Sabre\CalDAV\Subscriptions\Plugin() ); /* Calendar scheduling support */ $server->addPlugin( new Sabre\CalDAV\Schedule\Plugin() ); /* WebDAV-Sync plugin */ $server->addPlugin(new Sabre\DAV\Sync\Plugin()); // Support for html frontend $browser = new Sabre\DAV\Browser\Plugin(); $server->addPlugin($browser); // And off we go! $server->exec(); sabre-dav-2.1.10/examples/fileserver.php000066400000000000000000000027231267035675400201570ustar00rootroot00000000000000setBaseUri($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(); sabre-dav-2.1.10/examples/groupwareserver.php000066400000000000000000000054701267035675400212550ustar00rootroot00000000000000setAttribute(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 'vendor/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 = [ // /principals new \Sabre\CalDAV\Principal\Collection($principalBackend), // /calendars new \Sabre\CalDAV\CalendarRoot($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); if (isset($baseUri)) $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()); $server->addPlugin(new \Sabre\DAV\Sync\Plugin()); // And off we go! $server->exec(); sabre-dav-2.1.10/examples/simplefsserver.php000066400000000000000000000045461267035675400210670ustar00rootroot00000000000000myPath = $myPath; } function getChildren() { $children = []; // 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\Forbidden('Access denied'); if (is_dir($path)) { return new \MyCollection($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 \MyCollection($publicDir); // The rootNode needs to be passed to the server object. $server = new \Sabre\DAV\Server($rootNode); // And off we go! $server->exec(); sabre-dav-2.1.10/examples/sql/000077500000000000000000000000001267035675400160735ustar00rootroot00000000000000sabre-dav-2.1.10/examples/sql/mysql.addressbook.sql000066400000000000000000000020331267035675400222560ustar00rootroot00000000000000CREATE TABLE addressbooks ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, principaluri VARCHAR(255), displayname VARCHAR(255), uri VARCHAR(200), description TEXT, synctoken INT(11) UNSIGNED NOT NULL DEFAULT '1', UNIQUE(principaluri(100), uri(100)) ) 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, etag VARBINARY(32), size INT(11) UNSIGNED NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE addressbookchanges ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARCHAR(200) NOT NULL, synctoken INT(11) UNSIGNED NOT NULL, addressbookid INT(11) UNSIGNED NOT NULL, operation TINYINT(1) NOT NULL, INDEX addressbookid_synctoken (addressbookid, synctoken) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; sabre-dav-2.1.10/examples/sql/mysql.calendars.sql000066400000000000000000000042571267035675400217240ustar00rootroot00000000000000CREATE TABLE calendarobjects ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, calendardata MEDIUMBLOB, uri VARBINARY(200), calendarid INTEGER UNSIGNED NOT NULL, lastmodified INT(11) UNSIGNED, etag VARBINARY(32), size INT(11) UNSIGNED NOT NULL, componenttype VARBINARY(8), firstoccurence INT(11) UNSIGNED, lastoccurence INT(11) UNSIGNED, uid VARCHAR(200), 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 VARBINARY(100), displayname VARCHAR(100), uri VARBINARY(200), synctoken INTEGER UNSIGNED NOT NULL DEFAULT '1', description TEXT, calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0', calendarcolor VARBINARY(10), timezone TEXT, components VARBINARY(20), transparent TINYINT(1) NOT NULL DEFAULT '0', UNIQUE(principaluri, uri) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE calendarchanges ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARCHAR(200) NOT NULL, synctoken INT(11) UNSIGNED NOT NULL, calendarid INT(11) UNSIGNED NOT NULL, operation TINYINT(1) NOT NULL, INDEX calendarid_synctoken (calendarid, synctoken) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE calendarsubscriptions ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARCHAR(200) NOT NULL, principaluri VARCHAR(100) NOT NULL, source TEXT, displayname VARCHAR(100), refreshrate VARCHAR(10), calendarorder INT(11) UNSIGNED NOT NULL DEFAULT '0', calendarcolor VARCHAR(10), striptodos TINYINT(1) NULL, stripalarms TINYINT(1) NULL, stripattachments TINYINT(1) NULL, lastmodified INT(11) UNSIGNED, UNIQUE(principaluri, uri) ); CREATE TABLE schedulingobjects ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, principaluri VARCHAR(255), calendardata MEDIUMBLOB, uri VARCHAR(200), lastmodified INT(11) UNSIGNED, etag VARCHAR(32), size INT(11) UNSIGNED NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; sabre-dav-2.1.10/examples/sql/mysql.locks.sql000066400000000000000000000004401267035675400210710ustar00rootroot00000000000000CREATE TABLE locks ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, owner VARCHAR(100), timeout INTEGER UNSIGNED, created INTEGER, token VARBINARY(100), scope TINYINT, depth TINYINT, uri VARBINARY(1000), INDEX(token), INDEX(uri(100)) ); sabre-dav-2.1.10/examples/sql/mysql.principals.sql000066400000000000000000000012171267035675400221250ustar00rootroot00000000000000CREATE TABLE principals ( id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, uri VARCHAR(200) NOT NULL, email VARCHAR(80), displayname VARCHAR(80), vcardurl VARCHAR(255), 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); sabre-dav-2.1.10/examples/sql/mysql.propertystorage.sql000066400000000000000000000004021267035675400232250ustar00rootroot00000000000000CREATE TABLE propertystorage ( id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, path VARBINARY(1024) NOT NULL, name VARBINARY(100) NOT NULL, value MEDIUMBLOB ); CREATE UNIQUE INDEX path_property ON propertystorage (path(600), name(100)); sabre-dav-2.1.10/examples/sql/mysql.users.sql000066400000000000000000000003741267035675400211250ustar00rootroot00000000000000CREATE 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'); sabre-dav-2.1.10/examples/sql/pgsql.addressbook.sql000066400000000000000000000027031267035675400222430ustar00rootroot00000000000000CREATE TABLE addressbooks ( id SERIAL NOT NULL, principaluri VARCHAR(255), displayname VARCHAR(255), uri VARCHAR(200), description TEXT, synctoken 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, etag VARCHAR(32), size INTEGER NOT NULL ); 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; CREATE TABLE addressbookchanges ( id SERIAL NOT NULL, uri VARCHAR(200) NOT NULL, synctoken INTEGER NOT NULL, addressbookid INTEGER NOT NULL, operation SMALLINT NOT NULL ); ALTER TABLE ONLY addressbookchanges ADD CONSTRAINT addressbookchanges_pkey PRIMARY KEY (id); CREATE INDEX addressbookchanges_addressbookid_synctoken_ix ON addressbookchanges USING btree (addressbookid, synctoken); ALTER TABLE ONLY addressbookchanges ADD CONSTRAINT addressbookchanges_addressbookid_fkey FOREIGN KEY (addressbookid) REFERENCES addressbooks(id) ON DELETE CASCADE; sabre-dav-2.1.10/examples/sql/pgsql.calendars.sql000066400000000000000000000050511267035675400216760ustar00rootroot00000000000000CREATE TABLE calendars ( id SERIAL NOT NULL, principaluri VARCHAR(100), displayname VARCHAR(100), uri VARCHAR(200), synctoken INTEGER NOT NULL DEFAULT 1, description TEXT, calendarorder INTEGER NOT NULL DEFAULT 0, calendarcolor VARCHAR(10), timezone TEXT, components VARCHAR(20), uid VARCHAR(200), transparent SMALLINT NOT NULL DEFAULT '0' ); 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, calendardata TEXT, uri VARCHAR(200), calendarid INTEGER NOT NULL, lastmodified INTEGER, etag VARCHAR(32), size INTEGER NOT NULL, componenttype VARCHAR(8), 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; CREATE TABLE calendarsubscriptions ( id SERIAL NOT NULL, uri VARCHAR(200) NOT NULL, principaluri VARCHAR(100) NOT NULL, source TEXT, displayname VARCHAR(100), refreshrate VARCHAR(10), calendarorder INTEGER NOT NULL DEFAULT 0, calendarcolor VARCHAR(10), striptodos SMALLINT NULL, stripalarms SMALLINT NULL, stripattachments SMALLINT NULL, lastmodified INTEGER ); ALTER TABLE ONLY calendarsubscriptions ADD CONSTRAINT calendarsubscriptions_pkey PRIMARY KEY (id); CREATE UNIQUE INDEX calendarsubscriptions_ukey ON calendarsubscriptions USING btree (principaluri, uri); CREATE TABLE calendarchanges ( id SERIAL NOT NULL, uri VARCHAR(200) NOT NULL, synctoken INTEGER NOT NULL, calendarid INTEGER NOT NULL, operation SMALLINT NOT NULL DEFAULT 0 ); ALTER TABLE ONLY calendarchanges ADD CONSTRAINT calendarchanges_pkey PRIMARY KEY (id); CREATE INDEX calendarchanges_calendarid_synctoken_ix ON calendarchanges USING btree (calendarid, synctoken); ALTER TABLE ONLY calendarchanges ADD CONSTRAINT calendarchanges_calendar_fk FOREIGN KEY (calendarid) REFERENCES calendars(id) ON DELETE CASCADE; CREATE TABLE schedulingobjects ( id SERIAL NOT NULL, principaluri VARCHAR(255), calendardata BYTEA, uri VARCHAR(200), lastmodified INTEGER, etag VARCHAR(32), size INTEGER NOT NULL ); sabre-dav-2.1.10/examples/sql/pgsql.locks.sql000066400000000000000000000006011267035675400210510ustar00rootroot00000000000000CREATE 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); CREATE INDEX locks_token_ix ON locks USING btree (token); CREATE INDEX locks_uri_ix ON locks USING btree (uri); sabre-dav-2.1.10/examples/sql/pgsql.principals.sql000066400000000000000000000022171267035675400221070ustar00rootroot00000000000000CREATE TABLE principals ( id SERIAL NOT NULL, uri VARCHAR(200) NOT NULL, email VARCHAR(80), displayname VARCHAR(80), vcardurl VARCHAR(255) ); 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; ALTER TABLE ONLY groupmembers ADD CONSTRAINT groupmembers_member_id_id_fkey FOREIGN KEY (member_id) REFERENCES principals(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); sabre-dav-2.1.10/examples/sql/pgsql.propertystorage.sql000066400000000000000000000004651267035675400232170ustar00rootroot00000000000000CREATE TABLE propertystorage ( id SERIAL NOT NULL, path VARCHAR(1024) NOT NULL, name VARCHAR(100) NOT NULL, value TEXT ); ALTER TABLE ONLY propertystorage ADD CONSTRAINT propertystorage_pkey PRIMARY KEY (id); CREATE UNIQUE INDEX propertystorage_ukey ON propertystorage (path, name); sabre-dav-2.1.10/examples/sql/pgsql.users.sql000066400000000000000000000005161267035675400211040ustar00rootroot00000000000000CREATE TABLE users ( id SERIAL NOT NULL, username VARCHAR(50), digesta1 VARCHAR(32) ); 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'); sabre-dav-2.1.10/examples/sql/sqlite.addressbooks.sql000066400000000000000000000011111267035675400225710ustar00rootroot00000000000000CREATE TABLE addressbooks ( id integer primary key asc, principaluri text, displayname text, uri text, description text, synctoken integer ); CREATE TABLE cards ( id integer primary key asc, addressbookid integer, carddata blob, uri text, lastmodified integer, etag text, size integer ); CREATE TABLE addressbookchanges ( id integer primary key asc, uri text, synctoken integer, addressbookid integer, operation integer ); CREATE INDEX addressbookid_synctoken ON addressbookchanges (addressbookid, synctoken); sabre-dav-2.1.10/examples/sql/sqlite.calendars.sql000066400000000000000000000025211267035675400220500ustar00rootroot00000000000000CREATE 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, uid text ); CREATE TABLE calendars ( id integer primary key asc, principaluri text, displayname text, uri text, synctoken integer, description text, calendarorder integer, calendarcolor text, timezone text, components text, transparent bool ); CREATE TABLE calendarchanges ( id integer primary key asc, uri text, synctoken integer, calendarid integer, operation integer ); CREATE INDEX calendarid_synctoken ON calendarchanges (calendarid, synctoken); CREATE TABLE calendarsubscriptions ( id integer primary key asc, uri text, principaluri text, source text, displayname text, refreshrate text, calendarorder integer, calendarcolor text, striptodos bool, stripalarms bool, stripattachments bool, lastmodified int ); CREATE TABLE schedulingobjects ( id integer primary key asc, principaluri text, calendardata blob, uri text, lastmodified integer, etag text, size integer ); CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri); sabre-dav-2.1.10/examples/sql/sqlite.locks.sql000066400000000000000000000002701267035675400212260ustar00rootroot00000000000000BEGIN TRANSACTION; CREATE TABLE locks ( id integer primary key asc, owner text, timeout integer, created integer, token text, scope integer, depth integer, uri text ); COMMIT; sabre-dav-2.1.10/examples/sql/sqlite.principals.sql000066400000000000000000000011661267035675400222640ustar00rootroot00000000000000CREATE 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); sabre-dav-2.1.10/examples/sql/sqlite.propertystorage.sql000066400000000000000000000002641267035675400233670ustar00rootroot00000000000000CREATE TABLE propertystorage ( id integer primary key asc, path TEXT, name TEXT, value TEXT ); CREATE UNIQUE INDEX path_property ON propertystorage (path, name); sabre-dav-2.1.10/examples/sql/sqlite.users.sql000066400000000000000000000003051267035675400212530ustar00rootroot00000000000000CREATE TABLE users ( id integer primary key asc, username TEXT, digesta1 TEXT, UNIQUE(username) ); INSERT INTO users (username,digesta1) VALUES ('admin', '87fd274b7b6c01e48d7c2f965da8ddf7'); sabre-dav-2.1.10/examples/webserver/000077500000000000000000000000001267035675400173005ustar00rootroot00000000000000sabre-dav-2.1.10/examples/webserver/apache2_htaccess.conf000066400000000000000000000007601267035675400233320ustar00rootroot00000000000000RewriteEngine 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 sabre-dav-2.1.10/examples/webserver/apache2_vhost.conf000066400000000000000000000020241267035675400226730ustar00rootroot00000000000000# 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. # 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 sabre-dav-2.1.10/examples/webserver/apache2_vhost_cgi.conf000066400000000000000000000013601267035675400235170ustar00rootroot00000000000000# 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. # 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}] sabre-dav-2.1.10/lib/000077500000000000000000000000001267035675400142245ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CalDAV/000077500000000000000000000000001267035675400152565ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CalDAV/Backend/000077500000000000000000000000001267035675400166055ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CalDAV/Backend/AbstractBackend.php000066400000000000000000000172541267035675400223420ustar00rootroot00000000000000getCalendarObject($calendarId, $uri); }, $uris); } /** * 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 */ function calendarQuery($calendarId, array $filters) { $result = []; $objects = $this->getCalendarObjects($calendarId); 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 $filters * @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 = VObject\Reader::read($object['calendardata']); $validator = new CalDAV\CalendarQueryValidator(); return $validator->validate($vObject, $filters); } /** * Searches through all of a users calendars and calendar objects to find * an object with a specific UID. * * This method should return the path to this object, relative to the * calendar home, so this path usually only contains two parts: * * calendarpath/objectpath.ics * * If the uid is not found, return null. * * This method should only consider * objects that the principal owns, so * any calendars owned by other principals that also appear in this * collection should be ignored. * * @param string $principalUri * @param string $uid * @return string|null */ function getCalendarObjectByUID($principalUri, $uid) { // Note: this is a super slow naive implementation of this method. You // are highly recommended to optimize it, if your backend allows it. foreach($this->getCalendarsForUser($principalUri) as $calendar) { // We must ignore calendars owned by other principals. if ($calendar['principaluri']!==$principalUri) { continue; } // Ignore calendars that are shared. if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal']!==$principalUri) { continue; } $results = $this->calendarQuery( $calendar['id'], [ 'name' => 'VCALENDAR', 'prop-filters' => [], 'comp-filters' => [ [ 'name' => 'VEVENT', 'is-not-defined' => false, 'time-range' => null, 'comp-filters' => [], 'prop-filters' => [ [ 'name' => 'UID', 'is-not-defined' => false, 'time-range' => null, 'text-match' => [ 'value' => $uid, 'negate-condition' => false, 'collation' => 'i;octet', ], 'param-filters' => [], ], ] ] ], ] ); if ($results) { // We have a match return $calendar['uri'] . '/' . $results[0]; } } } } sabre-dav-2.1.10/lib/CalDAV/Backend/BackendInterface.php000066400000000000000000000235621267035675400224760ustar00rootroot00000000000000 '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', ]; /** * List of subscription properties, and how they map to database fieldnames. * * @var array */ public $subscriptionPropertyMap = [ '{DAV:}displayname' => 'displayname', '{http://apple.com/ns/ical/}refreshrate' => 'refreshrate', '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', '{http://calendarserver.org/ns/}subscribed-strip-todos' => 'striptodos', '{http://calendarserver.org/ns/}subscribed-strip-alarms' => 'stripalarms', '{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments', ]; /** * Creates the backend * * @param \PDO $pdo * @param string $calendarTableName * @param string $calendarObjectTableName * @param string $calendarChangesTable * @param string $schedulingObjectTable * @param string $calendarSubscriptionsTableName * @deprecated We are going to remove all the 'tableName' arguments and * move to public properties for those. Stop relying on them! */ function __construct(\PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects', $calendarChangesTableName = 'calendarchanges', $calendarSubscriptionsTableName = "calendarsubscriptions", $schedulingObjectTableName = "schedulingobjects") { $this->pdo = $pdo; $this->calendarTableName = $calendarTableName; $this->calendarObjectTableName = $calendarObjectTableName; $this->calendarChangesTableName = $calendarChangesTableName; $this->schedulingObjectTableName = $schedulingObjectTableName; $this->calendarSubscriptionsTableName = $calendarSubscriptionsTableName; } /** * 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. This is just the 'base uri' or 'filename' of the calendar. * * 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'. * * Many clients also require: * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set * For this property, you can just return an instance of * Sabre\CalDAV\Property\SupportedCalendarComponentSet. * * If you return {http://sabredav.org/ns}read-only and set the value to 1, * ACL will automatically be put in read-only mode. * * @param string $principalUri * @return array */ function getCalendarsForUser($principalUri) { $fields = array_values($this->propertyMap); $fields[] = 'id'; $fields[] = 'uri'; $fields[] = 'synctoken'; $fields[] = 'components'; $fields[] = 'principaluri'; $fields[] = 'transparent'; // 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([$principalUri]); $calendars = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $components = []; if ($row['components']) { $components = explode(',',$row['components']); } $calendar = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet($components), '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'), ]; 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 */ function createCalendar($principalUri, $calendarUri, array $properties) { $fieldNames = [ 'principaluri', 'uri', 'synctoken', 'transparent', ]; $values = [ ':principaluri' => $principalUri, ':uri' => $calendarUri, ':synctoken' => 1, ':transparent' => 0, ]; // 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 CalDAV\Property\SupportedCalendarComponentSet)) { throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Property\SupportedCalendarComponentSet'); } $values[':components'] = implode(',',$properties[$sccs]->getValue()); } $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; if (isset($properties[$transp])) { $values[':transparent'] = $properties[$transp]->getValue()==='transparent'; } 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 list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documenation for more info and examples. * * @param string $calendarId * @param \Sabre\DAV\PropPatch $propPatch * @return void */ function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) { $supportedProperties = array_keys($this->propertyMap); $supportedProperties[] = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; $propPatch->handle($supportedProperties, function($mutations) use ($calendarId) { $newValues = []; foreach($mutations as $propertyName=>$propertyValue) { switch($propertyName) { case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' : $fieldName = 'transparent'; $newValues[$fieldName] = $propertyValue->getValue()==='transparent'; break; default : $fieldName = $this->propertyMap[$propertyName]; $newValues[$fieldName] = $propertyValue; break; } } $valuesSql = []; foreach($newValues as $fieldName=>$value) { $valuesSql[] = $fieldName . ' = ?'; } $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ',$valuesSql) . " WHERE id = ?"); $newValues['id'] = $calendarId; $stmt->execute(array_values($newValues)); $this->addChange($calendarId, "", 2); return true; }); } /** * Delete a calendar and all it's objects * * @param string $calendarId * @return void */ function deleteCalendar($calendarId) { $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); $stmt->execute([$calendarId]); $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?'); $stmt->execute([$calendarId]); $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarChangesTableName.' WHERE calendarid = ?'); $stmt->execute([$calendarId]); } /** * Returns all calendar objects within a calendar. * * Every item contains an array with the following keys: * * calendardata - The iCalendar-compatible calendar data * * uri - a unique key which will be used to construct the uri. This can * be any arbitrary string, but making sure it ends with '.ics' is a * good idea. This is only the basename, or filename, not the full * path. * * lastmodified - a timestamp of the last modification time * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: * ' "abcdef"') * * size - The size of the calendar objects, in bytes. * * component - optional, a string containing the type of object, such * as 'vevent' or 'vtodo'. If specified, this will be used to populate * the Content-Type header. * * 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 */ function getCalendarObjects($calendarId) { $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); $stmt->execute([$calendarId]); $result = []; foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { $result[] = [ 'id' => $row['id'], 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'component' => strtolower($row['componenttype']), ]; } return $result; } /** * Returns information from a single calendar object, based on it's object * uri. * * The object uri is only the basename, or filename and not a full path. * * The returned array must have the same keys as getCalendarObjects. The * 'calendardata' object is required here though, while it's not required * for getCalendarObjects. * * This method must return null if the object did not exist. * * @param string $calendarId * @param string $objectUri * @return array|null */ function getCalendarObject($calendarId,$objectUri) { $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarId, $objectUri]); $row = $stmt->fetch(\PDO::FETCH_ASSOC); if(!$row) return null; return [ 'id' => $row['id'], 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'calendardata' => $row['calendardata'], 'component' => strtolower($row['componenttype']), ]; } /** * Returns a list of calendar objects. * * This method should work identical to getCalendarObject, but instead * return all the calendar objects in the list as an array. * * If the backend supports this, it may allow for some speed-ups. * * @param mixed $calendarId * @param array $uris * @return array */ function getMultipleCalendarObjects($calendarId, array $uris) { $query = 'SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri IN ('; // Inserting a whole bunch of question marks $query.=implode(',', array_fill(0, count($uris), '?')); $query.=')'; $stmt = $this->pdo->prepare($query); $stmt->execute(array_merge([$calendarId], $uris)); $result = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $result[] = [ 'id' => $row['id'], 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'calendardata' => $row['calendardata'], 'component' => strtolower($row['componenttype']), ]; } return $result; } /** * Creates a new calendar object. * * The object uri is only the basename, or filename and not a full path. * * 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 */ 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, uid) VALUES (?,?,?,?,?,?,?,?,?,?)'); $stmt->execute([ $calendarId, $objectUri, $calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], ]); $this->addChange($calendarId, $objectUri, 1); return '"' . $extraData['etag'] . '"'; } /** * Updates an existing calendarobject, based on it's uri. * * The object uri is only the basename, or filename and not a full path. * * 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 */ function updateCalendarObject($calendarId,$objectUri,$calendarData) { $extraData = $this->getDenormalizedData($calendarData); $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], $calendarId, $objectUri]); $this->addChange($calendarId, $objectUri, 2); return '"' . $extraData['etag'] . '"'; } /** * Parses some information from calendar objects, used for optimized * calendar-queries. * * Returns an array with the following keys: * * etag - An md5 checksum of the object without the quotes. * * size - Size of the object in bytes * * componentType - VEVENT, VTODO or VJOURNAL * * firstOccurence * * lastOccurence * * uid - value of the UID property * * @param string $calendarData * @return array */ protected function getDenormalizedData($calendarData) { $vObject = VObject\Reader::read($calendarData); $componentType = null; $component = null; $firstOccurence = null; $lastOccurence = null; $uid = null; foreach($vObject->getComponents() as $component) { if ($component->name!=='VTIMEZONE') { $componentType = $component->name; $uid = (string)$component->UID; 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(VObject\DateTimeParser::parse($component->DURATION->getValue())); $lastOccurence = $endDate->getTimeStamp(); } elseif (!$component->DTSTART->hasTime()) { $endDate = clone $component->DTSTART->getDateTime(); $endDate->modify('+1 day'); $lastOccurence = $endDate->getTimeStamp(); } else { $lastOccurence = $firstOccurence; } } else { $it = new 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 [ 'etag' => md5($calendarData), 'size' => strlen($calendarData), 'componentType' => $componentType, 'firstOccurence' => $firstOccurence, 'lastOccurence' => $lastOccurence, 'uid' => $uid, ]; } /** * Deletes an existing calendar object. * * The object uri is only the basename, or filename and not a full path. * * @param string $calendarId * @param string $objectUri * @return void */ function deleteCalendarObject($calendarId,$objectUri) { $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarId, $objectUri]); $this->addChange($calendarId, $objectUri, 3); } /** * 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 */ function calendarQuery($calendarId, array $filters) { $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 start time OR the end time is not specified, we can do a // 100% accurate mysql query. if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) { $requirePostFilter = false; } } } if ($requirePostFilter) { $query = "SELECT uri, calendardata FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; } else { $query = "SELECT uri FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid"; } $values = [ '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 = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { if ($requirePostFilter) { if (!$this->validateFilterForObject($row, $filters)) { continue; } } $result[] = $row['uri']; } return $result; } /** * Searches through all of a users calendars and calendar objects to find * an object with a specific UID. * * This method should return the path to this object, relative to the * calendar home, so this path usually only contains two parts: * * calendarpath/objectpath.ics * * If the uid is not found, return null. * * This method should only consider * objects that the principal owns, so * any calendars owned by other principals that also appear in this * collection should be ignored. * * @param string $principalUri * @param string $uid * @return string|null */ function getCalendarObjectByUID($principalUri, $uid) { $query = <<calendarObjectTableName AS calendarobjects LEFT JOIN $this->calendarTableName AS calendars ON calendarobjects.calendarid = calendars.id WHERE calendars.principaluri = ? AND calendarobjects.uid = ? SQL; $stmt = $this->pdo->prepare($query); $stmt->execute([$principalUri, $uid]); if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { return $row['calendaruri'] . '/' . $row['objecturi']; } } /** * The getChanges method returns all the changes that have happened, since * the specified syncToken in the specified calendar. * * This function should return an array, such as the following: * * [ * 'syncToken' => 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'modified.txt', * ], * 'deleted' => [ * 'foo.php.bak', * 'old.txt' * ] * ]; * * The returned syncToken property should reflect the *current* syncToken * of the calendar, as reported in the {http://sabredav.org/ns}sync-token * property this is needed here too, to ensure the operation is atomic. * * If the $syncToken argument is specified as null, this is an initial * sync, and all members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The $syncLevel argument is basically the 'depth' of the report. If it's * 1, you only have to report changes that happened only directly in * immediate descendants. If it's 2, it should also include changes from * the nodes below the child collections. (grandchildren) * * The $limit argument allows a client to specify how many results should * be returned at most. If the limit is not specified, it should be treated * as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $calendarId * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) { // Current synctoken $stmt = $this->pdo->prepare('SELECT synctoken FROM ' .$this->calendarTableName . ' WHERE id = ?'); $stmt->execute([ $calendarId ]); $currentToken = $stmt->fetchColumn(0); if (is_null($currentToken)) return null; $result = [ 'syncToken' => $currentToken, 'added' => [], 'modified' => [], 'deleted' => [], ]; if ($syncToken) { $query = "SELECT uri, operation FROM " . $this->calendarChangesTableName . " WHERE synctoken >= ? AND synctoken < ? AND calendarid = ? ORDER BY synctoken"; if ($limit>0) $query.= " LIMIT " . (int)$limit; // Fetching all changes $stmt = $this->pdo->prepare($query); $stmt->execute([$syncToken, $currentToken, $calendarId]); $changes = []; // This loop ensures that any duplicates are overwritten, only the // last change on a node is relevant. while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $changes[$row['uri']] = $row['operation']; } foreach($changes as $uri => $operation) { switch($operation) { case 1 : $result['added'][] = $uri; break; case 2 : $result['modified'][] = $uri; break; case 3 : $result['deleted'][] = $uri; break; } } } else { // No synctoken supplied, this is the initial sync. $query = "SELECT uri FROM " . $this->calendarObjectTableName . " WHERE calendarid = ?"; $stmt = $this->pdo->prepare($query); $stmt->execute([$calendarId]); $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN); } return $result; } /** * Adds a change record to the calendarchanges table. * * @param mixed $calendarId * @param string $objectUri * @param int $operation 1 = add, 2 = modify, 3 = delete. * @return void */ protected function addChange($calendarId, $objectUri, $operation) { $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarChangesTableName .' (uri, synctoken, calendarid, operation) SELECT ?, synctoken, ?, ? FROM ' . $this->calendarTableName .' WHERE id = ?'); $stmt->execute([ $objectUri, $calendarId, $operation, $calendarId ]); $stmt = $this->pdo->prepare('UPDATE ' . $this->calendarTableName . ' SET synctoken = synctoken + 1 WHERE id = ?'); $stmt->execute([ $calendarId ]); } /** * Returns a list of subscriptions for a principal. * * Every subscription is an array with the following keys: * * id, a unique id that will be used by other functions to modify the * subscription. This can be the same as the uri or a database key. * * uri. This is just the 'base uri' or 'filename' of the subscription. * * principaluri. The owner of the subscription. Almost always the same as * principalUri passed to this method. * * source. Url to the actual feed * * Furthermore, all the subscription info must be returned too: * * 1. {DAV:}displayname * 2. {http://apple.com/ns/ical/}refreshrate * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos * should not be stripped). * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms * should not be stripped). * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if * attachments should not be stripped). * 7. {http://apple.com/ns/ical/}calendar-color * 8. {http://apple.com/ns/ical/}calendar-order * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set * (should just be an instance of * Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of * default components). * * @param string $principalUri * @return array */ function getSubscriptionsForUser($principalUri) { $fields = array_values($this->subscriptionPropertyMap); $fields[] = 'id'; $fields[] = 'uri'; $fields[] = 'source'; $fields[] = 'principaluri'; $fields[] = 'lastmodified'; // Making fields a comma-delimited list $fields = implode(', ', $fields); $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM " . $this->calendarSubscriptionsTableName . " WHERE principaluri = ? ORDER BY calendarorder ASC"); $stmt->execute([$principalUri]); $subscriptions = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $subscription = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], 'source' => $row['source'], 'lastmodified' => $row['lastmodified'], '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']), ]; foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) { if (!is_null($row[$dbName])) { $subscription[$xmlName] = $row[$dbName]; } } $subscriptions[] = $subscription; } return $subscriptions; } /** * Creates a new subscription for a principal. * * If the creation was a success, an id must be returned that can be used to reference * this subscription in other methods, such as updateSubscription. * * @param string $principalUri * @param string $uri * @param array $properties * @return mixed */ function createSubscription($principalUri, $uri, array $properties) { $fieldNames = [ 'principaluri', 'uri', 'source', 'lastmodified', ]; if (!isset($properties['{http://calendarserver.org/ns/}source'])) { throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions'); } $values = [ ':principaluri' => $principalUri, ':uri' => $uri, ':source' => $properties['{http://calendarserver.org/ns/}source']->getHref(), ':lastmodified' => time(), ]; foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) { if (isset($properties[$xmlName])) { $values[':' . $dbName] = $properties[$xmlName]; $fieldNames[] = $dbName; } } $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarSubscriptionsTableName . " (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); $stmt->execute($values); return $this->pdo->lastInsertId(); } /** * Updates a subscription * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documenation for more info and examples. * * @param mixed $subscriptionId * @param \Sabre\DAV\PropPatch $propPatch * @return void */ function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) { $supportedProperties = array_keys($this->subscriptionPropertyMap); $supportedProperties[] = '{http://calendarserver.org/ns/}source'; $propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) { $newValues = []; foreach($mutations as $propertyName=>$propertyValue) { if ($propertyName === '{http://calendarserver.org/ns/}source') { $newValues['source'] = $propertyValue->getHref(); } else { $fieldName = $this->subscriptionPropertyMap[$propertyName]; $newValues[$fieldName] = $propertyValue; } } // Now we're generating the sql query. $valuesSql = []; foreach($newValues as $fieldName=>$value) { $valuesSql[] = $fieldName . ' = ?'; } $stmt = $this->pdo->prepare("UPDATE " . $this->calendarSubscriptionsTableName . " SET " . implode(', ',$valuesSql) . ", lastmodified = ? WHERE id = ?"); $newValues['lastmodified'] = time(); $newValues['id'] = $subscriptionId; $stmt->execute(array_values($newValues)); return true; }); } /** * Deletes a subscription * * @param mixed $subscriptionId * @return void */ function deleteSubscription($subscriptionId) { $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarSubscriptionsTableName . ' WHERE id = ?'); $stmt->execute([$subscriptionId]); } /** * Returns a single scheduling object. * * The returned array should contain the following elements: * * uri - A unique basename for the object. This will be used to * construct a full uri. * * calendardata - The iCalendar object * * lastmodified - The last modification date. Can be an int for a unix * timestamp, or a PHP DateTime object. * * etag - A unique token that must change if the object changed. * * size - The size of the object, in bytes. * * @param string $principalUri * @param string $objectUri * @return array */ function getSchedulingObject($principalUri, $objectUri) { $stmt = $this->pdo->prepare('SELECT uri, calendardata, lastmodified, etag, size FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ? AND uri = ?'); $stmt->execute([$principalUri, $objectUri]); $row = $stmt->fetch(\PDO::FETCH_ASSOC); if(!$row) return null; return [ 'uri' => $row['uri'], 'calendardata' => $row['calendardata'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'size' => (int)$row['size'], ]; } /** * Returns all scheduling objects for the inbox collection. * * These objects should be returned as an array. Every item in the array * should follow the same structure as returned from getSchedulingObject. * * The main difference is that 'calendardata' is optional. * * @param string $principalUri * @return array */ function getSchedulingObjects($principalUri) { $stmt = $this->pdo->prepare('SELECT id, calendardata, uri, lastmodified, etag, size FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ?'); $stmt->execute([$principalUri]); $result = []; foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { $result[] = [ 'calendardata' => $row['calendardata'], 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', 'size' => (int)$row['size'], ]; } return $result; } /** * Deletes a scheduling object * * @param string $principalUri * @param string $objectUri * @return void */ function deleteSchedulingObject($principalUri, $objectUri) { $stmt = $this->pdo->prepare('DELETE FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ? AND uri = ?'); $stmt->execute([$principalUri, $objectUri]); } /** * Creates a new scheduling object. This should land in a users' inbox. * * @param string $principalUri * @param string $objectUri * @param string $objectData * @return void */ function createSchedulingObject($principalUri, $objectUri, $objectData) { $stmt = $this->pdo->prepare('INSERT INTO '.$this->schedulingObjectTableName.' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)'); $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData) ]); } } sabre-dav-2.1.10/lib/CalDAV/Backend/SchedulingSupport.php000066400000000000000000000040661267035675400230060ustar00rootroot00000000000000 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'modified.txt', * ], * 'deleted' => [ * 'foo.php.bak', * 'old.txt' * ] * ); * * The returned syncToken property should reflect the *current* syncToken * of the calendar, as reported in the {http://sabredav.org/ns}sync-token * property This is * needed here too, to ensure the operation is atomic. * * If the $syncToken argument is specified as null, this is an initial * sync, and all members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The $syncLevel argument is basically the 'depth' of the report. If it's * 1, you only have to report changes that happened only directly in * immediate descendants. If it's 2, it should also include changes from * the nodes below the child collections. (grandchildren) * * The $limit argument allows a client to specify how many results should * be returned at most. If the limit is not specified, it should be treated * as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $calendarId * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null); } sabre-dav-2.1.10/lib/CalDAV/Calendar.php000066400000000000000000000337071267035675400175120ustar00rootroot00000000000000caldavBackend = $caldavBackend; $this->calendarInfo = $calendarInfo; } /** * Returns the name of the calendar * * @return string */ function getName() { return $this->calendarInfo['uri']; } /** * Updates properties on this node. * * This method received a PropPatch object, which contains all the * information about the update. * * To update specific properties, call the 'handle' method on this object. * Read the PropPatch documentation for more information. * * @param PropPatch $propPatch * @return void */ function propPatch(PropPatch $propPatch) { return $this->caldavBackend->updateCalendar($this->calendarInfo['id'], $propPatch); } /** * Returns the list of properties * * @param array $requestedProperties * @return array */ function getProperties($requestedProperties) { $response = []; foreach($requestedProperties as $prop) { if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop]; } return $response; } /** * Returns a calendar object * * The contained calendar objects are for example Events or Todo's. * * @param string $name * @return \Sabre\CalDAV\ICalendarObject */ function getChild($name) { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); if (!$obj) throw new DAV\Exception\NotFound('Calendar object not found'); $obj['acl'] = $this->getChildACL(); return new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); } /** * Returns the full list of calendar objects * * @return array */ function getChildren() { $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); $children = []; foreach($objs as $obj) { $obj['acl'] = $this->getChildACL(); $children[] = new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); } return $children; } /** * This method receives a list of paths in it's first argument. * It must return an array with Node objects. * * If any children are not found, you do not have to return them. * * @return array */ function getMultipleChildren(array $paths) { $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths); $children = []; foreach($objs as $obj) { $obj['acl'] = $this->getChildACL(); $children[] = new CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); } return $children; } /** * Checks if a child-node exists. * * @param string $name * @return bool */ 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 */ function createDirectory($name) { throw new 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 */ 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 */ 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 */ function setName($newName) { throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported'); } /** * Returns the last modification date as a unix timestamp. * * @return void */ 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 */ 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 */ 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 */ function getACL() { $acl = [ [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-read', 'protected' => true, ], [ 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', 'principal' => '{DAV:}authenticated', 'protected' => true, ], ]; if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) { $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner(), 'protected' => true, ]; $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ]; } return $acl; } /** * This method returns the ACL's for calendar objects in this calendar. * The result of this method automatically gets passed to the * calendar-object nodes in the calendar. * * @return array */ function getChildACL() { $acl = [ [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-read', 'protected' => true, ], ]; if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) { $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner(), 'protected' => true, ]; $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ]; } return $acl; } /** * Updates the ACL * * This method will receive a list of new ACE's. * * @param array $acl * @return void */ function setACL(array $acl) { throw new 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 */ function getSupportedPrivilegeSet() { $default = 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'][] = [ 'privilege' => '{' . 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 */ function calendarQuery(array $filters) { return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); } /** * This method returns the current sync-token for this collection. * This can be any string. * * If null is returned from this function, the plugin assumes there's no * sync information available. * * @return string|null */ function getSyncToken() { if ( $this->caldavBackend instanceof Backend\SyncSupport && isset($this->calendarInfo['{DAV:}sync-token']) ) { return $this->calendarInfo['{DAV:}sync-token']; } if ( $this->caldavBackend instanceof Backend\SyncSupport && isset($this->calendarInfo['{http://sabredav.org/ns}sync-token']) ) { return $this->calendarInfo['{http://sabredav.org/ns}sync-token']; } } /** * The getChanges method returns all the changes that have happened, since * the specified syncToken and the current collection. * * This function should return an array, such as the following: * * [ * 'syncToken' => 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'modified.txt', * ], * 'deleted' => [ * 'foo.php.bak', * 'old.txt' * ] * ]; * * The syncToken property should reflect the *current* syncToken of the * collection, as reported getSyncToken(). This is needed here too, to * ensure the operation is atomic. * * If the syncToken is specified as null, this is an initial sync, and all * members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The second argument is basically the 'depth' of the report. If it's 1, * you only have to report changes that happened only directly in immediate * descendants. If it's 2, it should also include changes from the nodes * below the child collections. (grandchildren) * * The third (optional) argument allows a client to specify how many * results should be returned at most. If the limit is not specified, it * should be treated as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChanges($syncToken, $syncLevel, $limit = null) { if (!$this->caldavBackend instanceof Backend\SyncSupport) { return null; } return $this->caldavBackend->getChangesForCalendar( $this->calendarInfo['id'], $syncToken, $syncLevel, $limit ); } } sabre-dav-2.1.10/lib/CalDAV/CalendarHome.php000066400000000000000000000302761267035675400203210ustar00rootroot00000000000000caldavBackend = $caldavBackend; $this->principalInfo = $principalInfo; } /** * Returns the name of this object * * @return string */ function getName() { list(,$name) = URLUtil::splitPath($this->principalInfo['uri']); return $name; } /** * Updates the name of this object * * @param string $name * @return void */ function setName($name) { throw new DAV\Exception\Forbidden(); } /** * Deletes this object * * @return void */ function delete() { throw new DAV\Exception\Forbidden(); } /** * Returns the last modification date * * @return int */ function getLastModified() { return null; } /** * Creates a new file under this object. * * This is currently not allowed * * @param string $filename * @param resource $data * @return void */ function createFile($filename, $data=null) { throw new 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 */ function createDirectory($filename) { throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); } /** * Returns a single calendar, by name * * @param string $name * @return Calendar */ function getChild($name) { // Special nodes if ($name === 'inbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) { return new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']); } if ($name === 'outbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) { return new Schedule\Outbox($this->principalInfo['uri']); } if ($name === 'notifications' && $this->caldavBackend instanceof Backend\NotificationSupport) { return new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); } // Calendars foreach($this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']) as $calendar) { if ($calendar['uri'] === $name) { if ($this->caldavBackend instanceof Backend\SharingSupport) { if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) { return new SharedCalendar($this->caldavBackend, $calendar); } else { return new ShareableCalendar($this->caldavBackend, $calendar); } } else { return new Calendar($this->caldavBackend, $calendar); } } } if ($this->caldavBackend instanceof Backend\SubscriptionSupport) { foreach($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) { if ($subscription['uri'] === $name) { return new Subscriptions\Subscription($this->caldavBackend, $subscription); } } } throw new NotFound('Node with name \'' . $name . '\' could not be found'); } /** * Checks if a calendar exists. * * @param string $name * @return bool */ function childExists($name) { try { return !!$this->getChild($name); } catch (NotFound $e) { return false; } } /** * Returns a list of calendars * * @return array */ function getChildren() { $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); $objs = []; foreach($calendars as $calendar) { if ($this->caldavBackend instanceof Backend\SharingSupport) { if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) { $objs[] = new SharedCalendar($this->caldavBackend, $calendar); } else { $objs[] = new ShareableCalendar($this->caldavBackend, $calendar); } } else { $objs[] = new Calendar($this->caldavBackend, $calendar); } } if ($this->caldavBackend instanceof Backend\SchedulingSupport) { $objs[] = new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']); $objs[] = new Schedule\Outbox($this->principalInfo['uri']); } // We're adding a notifications node, if it's supported by the backend. if ($this->caldavBackend instanceof Backend\NotificationSupport) { $objs[] = new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); } // If the backend supports subscriptions, we'll add those as well, if ($this->caldavBackend instanceof Backend\SubscriptionSupport) { foreach($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) { $objs[] = new Subscriptions\Subscription($this->caldavBackend, $subscription); } } return $objs; } /** * Creates a new calendar * * @param string $name * @param array $resourceType * @param array $properties * @return void */ function createExtendedCollection($name, array $resourceType, array $properties) { $isCalendar = false; $isSubscription = false; foreach($resourceType as $rt) { switch ($rt) { case '{DAV:}collection' : case '{http://calendarserver.org/ns/}shared-owner' : // ignore break; case '{urn:ietf:params:xml:ns:caldav}calendar' : $isCalendar = true; break; case '{http://calendarserver.org/ns/}subscribed' : $isSubscription = true; break; default : throw new DAV\Exception\InvalidResourceType('Unknown resourceType: ' . $rt); } } if ($isSubscription) { if (!$this->caldavBackend instanceof Backend\SubscriptionSupport) { throw new DAV\Exception\InvalidResourceType('This backend does not support subscriptions'); } $this->caldavBackend->createSubscription($this->principalInfo['uri'], $name, $properties); } elseif ($isCalendar) { $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties); } else { throw new DAV\Exception\InvalidResourceType('You can only create calendars and subscriptions in this collection'); } } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ 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 */ 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 */ function getACL() { return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->principalInfo['uri'], 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->principalInfo['uri'], 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', 'protected' => true, ], [ '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 */ function setACL(array $acl) { throw new 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 */ function getSupportedPrivilegeSet() { return null; } /** * This method is called when a user replied to a request to share. * * This method should return the url of the newly created calendar if the * share was accepted. * * @param string href The sharee who is replying (often a mailto: address) * @param int status One of the SharingPlugin::STATUS_* constants * @param string $calendarUri The url to the calendar thats being shared * @param string $inReplyTo The unique id this message is a response to * @param string $summary A description of the reply * @return null|string */ function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { if (!$this->caldavBackend instanceof Backend\SharingSupport) { throw new DAV\Exception\NotImplemented('Sharing support is not implemented by this backend.'); } return $this->caldavBackend->shareReply($href, $status, $calendarUri, $inReplyTo, $summary); } /** * Searches through all of a users calendars and calendar objects to find * an object with a specific UID. * * This method should return the path to this object, relative to the * calendar home, so this path usually only contains two parts: * * calendarpath/objectpath.ics * * If the uid is not found, return null. * * This method should only consider * objects that the principal owns, so * any calendars owned by other principals that also appear in this * collection should be ignored. * * @param string $uid * @return string|null */ function getCalendarObjectByUID($uid) { return $this->caldavBackend->getCalendarObjectByUID($this->principalInfo['uri'], $uid); } } sabre-dav-2.1.10/lib/CalDAV/CalendarObject.php000066400000000000000000000165261267035675400206410ustar00rootroot00000000000000caldavBackend = $caldavBackend; 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 */ function getName() { return $this->objectData['uri']; } /** * Returns the ICalendar-formatted object * * @return string */ 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->calendarInfo['id'], $this->objectData['uri']); } return $this->objectData['calendardata']; } /** * Updates the ICalendar-formatted object * * @param string|resource $calendarData * @return string */ 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 */ function delete() { $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']); } /** * Returns the mime content-type * * @return string */ function getContentType() { $mime = 'text/calendar; charset=utf-8'; if (isset($this->objectData['component']) && $this->objectData['component']) { $mime.='; component=' . $this->objectData['component']; } return $mime; } /** * Returns an ETag for this object. * * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. * * @return string */ 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 */ function getLastModified() { return $this->objectData['lastmodified']; } /** * Returns the size of this object in bytes * * @return int */ 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 */ 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 */ 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 */ function getACL() { // An alternative acl may be specified in the object data. if (isset($this->objectData['acl'])) { return $this->objectData['acl']; } // The default ACL return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->calendarInfo['principaluri'], 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->calendarInfo['principaluri'], 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', 'protected' => true, ], [ '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 */ 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 */ function getSupportedPrivilegeSet() { return null; } } sabre-dav-2.1.10/lib/CalDAV/CalendarQueryParser.php000066400000000000000000000210051267035675400217010ustar00rootroot00000000000000dom = $dom; $this->xpath = new \DOMXPath($dom); $this->xpath->registerNameSpace('cal',Plugin::NS_CALDAV); $this->xpath->registerNameSpace('dav','urn:DAV'); } /** * Parses the request. * * @return void */ function parse() { $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 = []; for($ii=0; $ii < $compFilterNodes->length; $ii++) { $compFilterNode = $compFilterNodes->item($ii); $compFilter = []; $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'], [ '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 = []; for ($ii=0; $ii < $propFilterNodes->length; $ii++) { $propFilterNode = $propFilterNodes->item($ii); $propFilter = []; $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 = []; for($ii=0;$ii<$paramFilterNodes->length;$ii++) { $paramFilterNode = $paramFilterNodes->item($ii); $paramFilter = []; $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 [ '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 = VObject\DateTimeParser::parseDateTime($start); } else { $start = null; } if ($end = $timeRangeNode->getAttribute('end')) { $end = 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 [ 'start' => $start, 'end' => $end, ]; } /** * Parses the CALDAV:expand element. * * This method returns an array with two elements: start and end. Each are * a PHP DateTime object. * * @param \DOMElement $parentNode * @return array */ 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 = 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 = 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 [ 'start' => $start, 'end' => $end, ]; } } sabre-dav-2.1.10/lib/CalDAV/CalendarQueryValidator.php000066400000000000000000000306351267035675400224030ustar00rootroot00000000000000name !== $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 VObject\Component $parent * @param array $filters * @return bool */ protected function validateCompFilters(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 VObject\Component $parent * @param array $filters * @return bool */ protected function validatePropFilters(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 VObject\Property $parent * @param array $filters * @return bool */ protected function validateParamFilters(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']]->getParts() as $paramPart) { if($this->validateTextMatch($paramPart,$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 VObject\Node|string $check Value to check against. * @param array $textMatch * @return bool */ protected function validateTextMatch($check, array $textMatch) { if ($check instanceof VObject\Node) { $check = $check->getValue(); } $isMatching = \Sabre\DAV\StringUtil::textMatch($check, $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 VObject\Node $component * @param DateTime $start * @param DateTime $end * @return bool */ protected function validateTimeRange(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 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; if ($expandedEvent->VALARM !== 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'); } } } sabre-dav-2.1.10/lib/CalDAV/CalendarRoot.php000066400000000000000000000043251267035675400203500ustar00rootroot00000000000000caldavBackend = $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 */ function getName() { return 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 */ function getChildForPrincipal(array $principal) { return new CalendarHome($this->caldavBackend, $principal); } } sabre-dav-2.1.10/lib/CalDAV/CalendarRootNode.php000066400000000000000000000010071267035675400211500ustar00rootroot00000000000000ownerDocument; $np = $doc->createElementNS(CalDAV\Plugin::NS_CALDAV,'cal:supported-calendar-component'); $errorNode->appendChild($np); } } sabre-dav-2.1.10/lib/CalDAV/ICSExportPlugin.php000066400000000000000000000241471267035675400207760ustar00rootroot00000000000000server = $server; $this->server->on('method:GET', [$this,'httpGet'], 90); } /** * Intercepts GET requests on calendar urls ending with ?export. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpGet(RequestInterface $request, ResponseInterface $response) { $queryParams = $request->getQueryParameters(); if (!array_key_exists('export', $queryParams)) return; $path = $request->getPath(); $node = $this->server->getProperties($path, [ '{DAV:}resourcetype', '{DAV:}displayname', '{http://sabredav.org/ns}sync-token', '{DAV:}sync-token', '{http://apple.com/ns/ical/}calendar-color', ]); if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{' . Plugin::NS_CALDAV . '}calendar')) { return; } // Marking the transactionType, for logging purposes. $this->server->transactionType = 'get-calendar-export'; $properties = $node; $start = null; $end = null; $expand = false; if (isset($queryParams['start'])) { if (!ctype_digit($queryParams['start'])) { throw new BadRequest('The start= parameter must contain a unix timestamp'); } $start = DateTime::createFromFormat('U', $queryParams['start']); } if (isset($queryParams['end'])) { if (!ctype_digit($queryParams['end'])) { throw new BadRequest('The end= parameter must contain a unix timestamp'); } $end = DateTime::createFromFormat('U', $queryParams['end']); } if (isset($queryParams['expand']) && !!$queryParams['expand']) { if (!$start || !$end) { throw new BadRequest('If you\'d like to expand recurrences, you must specify both a start= and end= parameter.'); } $expand = true; } $format = \Sabre\HTTP\Util::Negotiate( $request->getHeader('Accept'), [ 'text/calendar', 'application/calendar+json', ] ); if (isset($queryParams['accept'])) { if ($queryParams['accept'] === 'application/calendar+json' || $queryParams['accept'] === 'jcal') { $format = 'application/calendar+json'; } } if (!$format) { $format = 'text/calendar'; } $this->generateResponse($path, $start, $end, $expand, $format, $properties, $response); // Returning false to break the event chain return false; } /** * This method is responsible for generating the actual, full response. * * @param string $path * @param DateTime|null $start * @param DateTime|null $end * @param bool $expand * @param string $format * @param array $properties * @param ResponseInterface $response */ protected function generateResponse($path, $start, $end, $expand, $format, $properties, ResponseInterface $response) { $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data'; $blobs = []; if ($start || $end) { // If there was a start or end filter, we need to enlist // calendarQuery for speed. $calendarNode = $this->server->tree->getNodeForPath($path); $queryResult = $calendarNode->calendarQuery([ 'name' => 'VCALENDAR', 'comp-filters' => [ [ 'name' => 'VEVENT', 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => [ 'start' => $start, 'end' => $end, ], ], ], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => null, ]); // queryResult is just a list of base urls. We need to prefix the // calendar path. $queryResult = array_map( function($item) use ($path) { return $path . '/' . $item; }, $queryResult ); $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]); unset($queryResult); } else { $nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1); } // Flattening the arrays foreach($nodes as $node) { if (isset($node[200][$calDataProp])) { $blobs[] = $node[200][$calDataProp]; } } unset($nodes); $mergedCalendar = $this->mergeObjects( $properties, $blobs ); if ($expand) { $calendarTimeZone = null; // We're expanding, and for that we need to figure out the // calendar's timezone. $tzProp = '{' . Plugin::NS_CALDAV . '}calendar-timezone'; $tzResult = $this->server->getProperties($path, [$tzProp]); if (isset($tzResult[$tzProp])) { // This property contains a VCALENDAR with a single // VTIMEZONE. $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]); $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); unset($vtimezoneObj); } else { // Defaulting to UTC. $calendarTimeZone = new DateTimeZone('UTC'); } $mergedCalendar->expand($start, $end, $calendarTimeZone); } $response->setHeader('Content-Type', $format); switch($format) { case 'text/calendar' : $mergedCalendar = $mergedCalendar->serialize(); break; case 'application/calendar+json' : $mergedCalendar = json_encode($mergedCalendar->jsonSerialize()); break; } $response->setStatus(200); $response->setBody($mergedCalendar); } /** * Merges all calendar objects, and builds one big iCalendar blob. * * @param array $properties Some CalDAV properties * @param array $inputObjects * @return VObject\Component\VCalendar */ function mergeObjects(array $properties, array $inputObjects) { $calendar = new VObject\Component\VCalendar(); $calendar->version = '2.0'; if (DAV\Server::$exposeVersion) { $calendar->prodid = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; } else { $calendar->prodid = '-//SabreDAV//SabreDAV//EN'; } if (isset($properties['{DAV:}displayname'])) { $calendar->{'X-WR-CALNAME'} = $properties['{DAV:}displayname']; } if (isset($properties['{http://apple.com/ns/ical/}calendar-color'])) { $calendar->{'X-APPLE-CALENDAR-COLOR'} = $properties['{http://apple.com/ns/ical/}calendar-color']; } $collectedTimezones = []; $timezones = []; $objects = []; foreach($inputObjects as $inputObject) { $nodeComp = VObject\Reader::read($inputObject); 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; } } sabre-dav-2.1.10/lib/CalDAV/ICalendar.php000066400000000000000000000006341267035675400176140ustar00rootroot00000000000000caldavBackend = $caldavBackend; $this->principalUri = $principalUri; } /** * Returns all notifications for a principal * * @return array */ function getChildren() { $children = []; $notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri); foreach($notifications as $notification) { $children[] = new Node( $this->caldavBackend, $this->principalUri, $notification ); } return $children; } /** * Returns the name of this object * * @return string */ function getName() { return 'notifications'; } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ 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 */ 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 */ function getACL() { return [ [ 'principal' => $this->getOwner(), 'privilege' => '{DAV:}read', 'protected' => true, ], [ 'principal' => $this->getOwner(), 'privilege' => '{DAV:}write', 'protected' => true, ] ]; } /** * Updates the ACL * * This method will receive a list of new ACE's as an array argument. * * @param array $acl * @return void */ function setACL(array $acl) { throw new DAV\Exception\NotImplemented('Updating ACLs is not implemented here'); } /** * 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 */ function getSupportedPrivilegeSet() { return null; } } sabre-dav-2.1.10/lib/CalDAV/Notifications/ICollection.php000066400000000000000000000011461267035675400230060ustar00rootroot00000000000000caldavBackend = $caldavBackend; $this->principalUri = $principalUri; $this->notification = $notification; } /** * Returns the path name for this notification * * @return id */ function getName() { return $this->notification->getId() . '.xml'; } /** * Returns the etag for the notification. * * The etag must be surrounded by litteral double-quotes. * * @return string */ function getETag() { return $this->notification->getETag(); } /** * This method must return an xml element, using the * Sabre\CalDAV\Notifications\INotificationType classes. * * @return INotificationType */ function getNotificationType() { return $this->notification; } /** * Deletes this notification * * @return void */ function delete() { $this->caldavBackend->deleteNotification($this->getOwner(), $this->notification); } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ 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 */ 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 */ function getACL() { return [ [ 'principal' => $this->getOwner(), 'privilege' => '{DAV:}read', 'protected' => true, ], [ 'principal' => $this->getOwner(), 'privilege' => '{DAV:}write', 'protected' => true, ] ]; } /** * Updates the ACL * * This method will receive a list of new ACE's as an array argument. * * @param array $acl * @return void */ function setACL(array $acl) { throw new DAV\Exception\NotImplemented('Updating ACLs is not implemented here'); } /** * 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 */ function getSupportedPrivilegeSet() { return null; } } sabre-dav-2.1.10/lib/CalDAV/Notifications/Notification/000077500000000000000000000000001267035675400225155ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CalDAV/Notifications/Notification/Invite.php000066400000000000000000000222531267035675400244700ustar00rootroot00000000000000$value) { if (!property_exists($this, $key)) { throw new \InvalidArgumentException('Unknown option: ' . $key); } $this->$key = $value; } } /** * Serializes the notification as a single property. * * You should usually just encode the single top-level element of the * notification. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server, \DOMElement $node) { $prop = $node->ownerDocument->createElement('cs:invite-notification'); $node->appendChild($prop); } /** * This method serializes the entire notification, as it is used in the * response body. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serializeBody(DAV\Server $server, \DOMElement $node) { $doc = $node->ownerDocument; $dt = $doc->createElement('cs:dtstamp'); $this->dtStamp->setTimezone(new \DateTimezone('GMT')); $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); $node->appendChild($dt); $prop = $doc->createElement('cs:invite-notification'); $node->appendChild($prop); $uid = $doc->createElement('cs:uid'); $uid->appendChild( $doc->createTextNode($this->id) ); $prop->appendChild($uid); $href = $doc->createElement('d:href'); $href->appendChild( $doc->createTextNode( $this->href ) ); $prop->appendChild($href); $nodeName = null; switch($this->type) { case SharingPlugin::STATUS_ACCEPTED : $nodeName = 'cs:invite-accepted'; break; case SharingPlugin::STATUS_DECLINED : $nodeName = 'cs:invite-declined'; break; case SharingPlugin::STATUS_DELETED : $nodeName = 'cs:invite-deleted'; break; case SharingPlugin::STATUS_NORESPONSE : $nodeName = 'cs:invite-noresponse'; break; } $prop->appendChild( $doc->createElement($nodeName) ); $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); $hostUrl = $doc->createElement('cs:hosturl'); $hostUrl->appendChild($hostHref); $prop->appendChild($hostUrl); $access = $doc->createElement('cs:access'); if ($this->readOnly) { $access->appendChild($doc->createElement('cs:read')); } else { $access->appendChild($doc->createElement('cs:read-write')); } $prop->appendChild($access); $organizerUrl = $doc->createElement('cs:organizer'); // If the organizer contains a 'mailto:' part, it means it should be // treated as absolute. if (strtolower(substr($this->organizer,0,7))==='mailto:') { $organizerHref = new DAV\Property\Href($this->organizer, false); } else { $organizerHref = new DAV\Property\Href($this->organizer, true); } $organizerHref->serialize($server, $organizerUrl); if ($this->commonName) { $commonName = $doc->createElement('cs:common-name'); $commonName->appendChild($doc->createTextNode($this->commonName)); $organizerUrl->appendChild($commonName); $commonNameOld = $doc->createElement('cs:organizer-cn'); $commonNameOld->appendChild($doc->createTextNode($this->commonName)); $prop->appendChild($commonNameOld); } if ($this->firstName) { $firstName = $doc->createElement('cs:first-name'); $firstName->appendChild($doc->createTextNode($this->firstName)); $organizerUrl->appendChild($firstName); $firstNameOld = $doc->createElement('cs:organizer-first'); $firstNameOld->appendChild($doc->createTextNode($this->firstName)); $prop->appendChild($firstNameOld); } if ($this->lastName) { $lastName = $doc->createElement('cs:last-name'); $lastName->appendChild($doc->createTextNode($this->lastName)); $organizerUrl->appendChild($lastName); $lastNameOld = $doc->createElement('cs:organizer-last'); $lastNameOld->appendChild($doc->createTextNode($this->lastName)); $prop->appendChild($lastNameOld); } $prop->appendChild($organizerUrl); if ($this->summary) { $summary = $doc->createElement('cs:summary'); $summary->appendChild($doc->createTextNode($this->summary)); $prop->appendChild($summary); } if ($this->supportedComponents) { $xcomp = $doc->createElement('cal:supported-calendar-component-set'); $this->supportedComponents->serialize($server, $xcomp); $prop->appendChild($xcomp); } } /** * Returns a unique id for this notification * * This is just the base url. This should generally be some kind of unique * id. * * @return string */ function getId() { return $this->id; } /** * Returns the ETag for this notification. * * The ETag must be surrounded by literal double-quotes. * * @return string */ function getETag() { return $this->etag; } } sabre-dav-2.1.10/lib/CalDAV/Notifications/Notification/InviteReply.php000066400000000000000000000130341267035675400255010ustar00rootroot00000000000000$value) { if (!property_exists($this, $key)) { throw new \InvalidArgumentException('Unknown option: ' . $key); } $this->$key = $value; } } /** * Serializes the notification as a single property. * * You should usually just encode the single top-level element of the * notification. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server, \DOMElement $node) { $prop = $node->ownerDocument->createElement('cs:invite-reply'); $node->appendChild($prop); } /** * This method serializes the entire notification, as it is used in the * response body. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serializeBody(DAV\Server $server, \DOMElement $node) { $doc = $node->ownerDocument; $dt = $doc->createElement('cs:dtstamp'); $this->dtStamp->setTimezone(new \DateTimezone('GMT')); $dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z'))); $node->appendChild($dt); $prop = $doc->createElement('cs:invite-reply'); $node->appendChild($prop); $uid = $doc->createElement('cs:uid'); $uid->appendChild($doc->createTextNode($this->id)); $prop->appendChild($uid); $inReplyTo = $doc->createElement('cs:in-reply-to'); $inReplyTo->appendChild( $doc->createTextNode($this->inReplyTo) ); $prop->appendChild($inReplyTo); $href = $doc->createElement('d:href'); $href->appendChild( $doc->createTextNode($this->href) ); $prop->appendChild($href); $nodeName = null; switch($this->type) { case SharingPlugin::STATUS_ACCEPTED : $nodeName = 'cs:invite-accepted'; break; case SharingPlugin::STATUS_DECLINED : $nodeName = 'cs:invite-declined'; break; } $prop->appendChild( $doc->createElement($nodeName) ); $hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl); $hostUrl = $doc->createElement('cs:hosturl'); $hostUrl->appendChild($hostHref); $prop->appendChild($hostUrl); if ($this->summary) { $summary = $doc->createElement('cs:summary'); $summary->appendChild($doc->createTextNode($this->summary)); $prop->appendChild($summary); } } /** * Returns a unique id for this notification * * This is just the base url. This should generally be some kind of unique * id. * * @return string */ function getId() { return $this->id; } /** * Returns the ETag for this notification. * * The ETag must be surrounded by literal double-quotes. * * @return string */ function getETag() { return $this->etag; } } sabre-dav-2.1.10/lib/CalDAV/Notifications/Notification/SystemStatus.php000066400000000000000000000102331267035675400257150ustar00rootroot00000000000000id = $id; $this->type = $type; $this->description = $description; $this->href = $href; $this->etag = $etag; } /** * Serializes the notification as a single property. * * You should usually just encode the single top-level element of the * notification. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server, \DOMElement $node) { switch($this->type) { case self::TYPE_LOW : $type = 'low'; break; case self::TYPE_MEDIUM : $type = 'medium'; break; default : case self::TYPE_HIGH : $type = 'high'; break; } $prop = $node->ownerDocument->createElement('cs:systemstatus'); $prop->setAttribute('type', $type); $node->appendChild($prop); } /** * This method serializes the entire notification, as it is used in the * response body. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serializeBody(DAV\Server $server, \DOMElement $node) { switch($this->type) { case self::TYPE_LOW : $type = 'low'; break; case self::TYPE_MEDIUM : $type = 'medium'; break; default : case self::TYPE_HIGH : $type = 'high'; break; } $prop = $node->ownerDocument->createElement('cs:systemstatus'); $prop->setAttribute('type', $type); if ($this->description) { $text = $node->ownerDocument->createTextNode($this->description); $desc = $node->ownerDocument->createElement('cs:description'); $desc->appendChild($text); $prop->appendChild($desc); } if ($this->href) { $text = $node->ownerDocument->createTextNode($this->href); $href = $node->ownerDocument->createElement('d:href'); $href->appendChild($text); $prop->appendChild($href); } $node->appendChild($prop); } /** * Returns a unique id for this notification * * This is just the base url. This should generally be some kind of unique * id. * * @return string */ function getId() { return $this->id; } /* * Returns the ETag for this notification. * * The ETag must be surrounded by literal double-quotes. * * @return string */ function getETag() { return $this->etag; } } sabre-dav-2.1.10/lib/CalDAV/Notifications/Plugin.php000066400000000000000000000104721267035675400220420ustar00rootroot00000000000000server = $server; $server->on('method:GET', [$this,'httpGet'], 90); $server->on('propFind', [$this,'propFind']); $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification'; array_push($server->protectedProperties, '{' . self::NS_CALENDARSERVER . '}notification-URL', '{' . self::NS_CALENDARSERVER . '}notificationtype' ); } /** * PropFind * * @param PropFind $propFind * @param BaseINode $node * @return void */ function propFind(PropFind $propFind, BaseINode $node) { $caldavPlugin = $this->server->getPlugin('caldav'); if ($node instanceof DAVACL\IPrincipal) { $principalUrl = $node->getPrincipalUrl(); // notification-URL property $propFind->handle('{' . self::NS_CALENDARSERVER . '}notification-URL', function() use ($principalUrl, $caldavPlugin) { $notificationPath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl) . '/notifications/'; return new DAV\Property\Href($notificationPath); }); } // instanceof IPrincipal if ($node instanceof INode) { $propFind->handle( '{' . self::NS_CALENDARSERVER . '}notificationtype', [$node, 'getNotificationType'] ); } // instanceof Notifications_INode } /** * This event is triggered before the usual GET request handler. * * We use this to intercept GET calls to notification nodes, and return the * proper response. * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function httpGet(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); try { $node = $this->server->tree->getNodeForPath($path); } catch (DAV\Exception\NotFound $e) { return; } if (!$node instanceof INode) return; $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $root = $dom->createElement('cs:notification'); foreach($this->server->xmlNamespaces as $namespace => $prefix) { $root->setAttribute('xmlns:' . $prefix, $namespace); } $dom->appendChild($root); $node->getNotificationType()->serializeBody($this->server, $root); $response->setHeader('Content-Type','application/xml'); $response->setHeader('ETag',$node->getETag()); $response->setStatus(200); $response->setBody($dom->saveXML()); // Return false to break the event chain. return false; } } sabre-dav-2.1.10/lib/CalDAV/Plugin.php000066400000000000000000001125201267035675400172260ustar00rootroot00000000000000server->tree->getNodeForPath($parent); if ($node instanceof DAV\IExtendedCollection) { try { $node->getChild($name); } catch (DAV\Exception\NotFound $e) { return ['MKCALENDAR']; } } return []; } /** * Returns the path to a principal's calendar home. * * The return url must not end with a slash. * * @param string $principalUrl * @return string */ function getCalendarHomeForPrincipal($principalUrl) { // The default is a bit naive, but it can be overwritten. list(, $nodeName) = URLUtil::splitPath($principalUrl); return self::CALENDAR_ROOT . '/' . $nodeName; } /** * Returns a list of features for the DAV: HTTP header. * * @return array */ function getFeatures() { return ['calendar-access', 'calendar-proxy']; } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using DAV\Server::getPlugin * * @return string */ 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 */ function getSupportedReportSet($uri) { $node = $this->server->tree->getNodeForPath($uri); $reports = []; if ($node instanceof ICalendarObjectContainer || $node instanceof ICalendarObject) { $reports[] = '{' . self::NS_CALDAV . '}calendar-multiget'; $reports[] = '{' . self::NS_CALDAV . '}calendar-query'; } if ($node instanceof ICalendar) { $reports[] = '{' . self::NS_CALDAV . '}free-busy-query'; } // iCal has a bug where it assumes that sync support is enabled, only // if we say we support it on the calendar-home, even though this is // not actually the case. if ($node instanceof CalendarHome && $this->server->getPlugin('sync')) { $reports[] = '{DAV:}sync-collection'; } return $reports; } /** * Initializes the plugin * * @param DAV\Server $server * @return void */ function initialize(DAV\Server $server) { $this->server = $server; $server->on('method:MKCALENDAR', [$this,'httpMkcalendar']); $server->on('report', [$this,'report']); $server->on('propFind', [$this,'propFind']); $server->on('onHTMLActionsPanel', [$this,'htmlActionsPanel']); $server->on('onBrowserPostAction', [$this,'browserPostAction']); $server->on('beforeCreateFile', [$this,'beforeCreateFile']); $server->on('beforeWriteContent', [$this,'beforeWriteContent']); $server->on('afterMethod:GET', [$this,'httpAfterGET']); $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->propertyMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Property\\ScheduleCalendarTransp'; $server->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = '{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', // CalendarServer extensions '{' . self::NS_CALENDARSERVER . '}getctag', '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for', '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for' ); if ($aclPlugin = $server->getPlugin('acl')) { $aclPlugin->principalSearchPropertySet['{' . self::NS_CALDAV . '}calendar-user-address-set'] = 'Calendar address'; } } /** * This functions handles REPORT requests specific to CalDAV * * @param string $reportName * @param \DOMNode $dom * @return bool */ function report($reportName,$dom) { switch($reportName) { case '{' . self::NS_CALDAV . '}calendar-multiget' : $this->server->transactionType = 'report-calendar-multiget'; $this->calendarMultiGetReport($dom); return false; case '{' . self::NS_CALDAV . '}calendar-query' : $this->server->transactionType = 'report-calendar-query'; $this->calendarQueryReport($dom); return false; case '{' . self::NS_CALDAV . '}free-busy-query' : $this->server->transactionType = 'report-free-busy-query'; $this->freeBusyQueryReport($dom); return false; } } /** * This function handles the MKCALENDAR HTTP method, which creates * a new calendar. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpMkCalendar(RequestInterface $request, ResponseInterface $response) { // 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 = $request->getBodyAsString(); $path = $request->getPath(); $properties = []; if ($body) { $dom = DAV\XMLUtil::loadDOMDocument($body); foreach($dom->firstChild->childNodes as $child) { if (DAV\XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue; foreach(DAV\XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) { $properties[$k] = $prop; } } } // iCal abuses MKCALENDAR since iCal 10.9.2 to create server-stored // subscriptions. Before that it used MKCOL which was the correct way // to do this. // // If the body had a {DAV:}resourcetype, it means we stumbled upon this // request, and we simply use it instead of the pre-defined list. if (isset($properties['{DAV:}resourcetype'])) { $resourceType = $properties['{DAV:}resourcetype']->getValue(); } else { $resourceType = ['{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar']; } $this->server->createCollection($path,$resourceType,$properties); $this->server->httpResponse->setStatus(201); $this->server->httpResponse->setHeader('Content-Length',0); // This breaks the method chain. return false; } /** * PropFind * * 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 DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFind(DAV\PropFind $propFind, DAV\INode $node) { $ns = '{' . self::NS_CALDAV . '}'; if ($node instanceof ICalendarObjectContainer) { $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize); $propFind->handle($ns . 'supported-calendar-data', function() { return new Property\SupportedCalendarData(); }); $propFind->handle($ns . 'supported-collation-set', function() { return new Property\SupportedCollationSet(); }); } if ($node instanceof DAVACL\IPrincipal) { $principalUrl = $node->getPrincipalUrl(); $propFind->handle('{' . self::NS_CALDAV . '}calendar-home-set', function() use ($principalUrl) { $calendarHomePath = $this->getCalendarHomeForPrincipal($principalUrl) . '/'; return new DAV\Property\Href($calendarHomePath); }); // The calendar-user-address-set property is basically mapped to // the {DAV:}alternate-URI-set property. $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-address-set', function() use ($node) { $addresses = $node->getAlternateUriSet(); $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . '/'; return new HrefList($addresses, false); }); // For some reason somebody thought it was a good idea to add // another one of these properties. We're supporting it too. $propFind->handle('{' . self::NS_CALENDARSERVER . '}email-address-set', function() use ($node) { $addresses = $node->getAlternateUriSet(); $emails = []; foreach($addresses as $address) { if (substr($address,0,7)==='mailto:') { $emails[] = substr($address,7); } } return new Property\EmailAddressSet($emails); }); // 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 ($propFind->getStatus($propRead)===404 || $propFind->getStatus($propWrite)===404) { $aclPlugin = $this->server->getPlugin('acl'); $membership = $aclPlugin->getPrincipalMembership($propFind->getPath()); $readList = []; $writeList = []; 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 Principal\IProxyRead) { list($readList[]) = URLUtil::splitPath($group); } if ($groupNode instanceof Principal\IProxyWrite) { list($writeList[]) = URLUtil::splitPath($group); } } $propFind->set($propRead, new HrefList($readList)); $propFind->set($propWrite, new HrefList($writeList)); } } // instanceof IPrincipal if ($node instanceof 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. $propFind->handle( '{' . Plugin::NS_CALDAV . '}calendar-data', function() use ($node) { $val = $node->get(); if (is_resource($val)) $val = stream_get_contents($val); // Taking out \r to not screw up the xml output return 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 */ function calendarMultiGetReport($dom) { $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)); $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); $xpath = new \DOMXPath($dom); $xpath->registerNameSpace('cal',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 DAV\Exception\BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element'); } $start = VObject\DateTimeParser::parseDateTime($start); $end = VObject\DateTimeParser::parseDateTime($end); if ($end <= $start) { throw new DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.'); } $expand = true; } else { $expand = false; } $needsJson = $xpath->evaluate("boolean(/cal:calendar-multiget/dav:prop/cal:calendar-data[@content-type='application/calendar+json'])"); $uris = []; foreach($hrefElems as $elem) { $uris[] = $this->server->calculateUri($elem->nodeValue); } $tz = null; $timeZones = []; foreach($this->server->getPropertiesForMultiplePaths($uris, $properties) as $uri=>$objProps) { if (($needsJson || $expand) && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); if ($expand) { // We're expanding, and for that we need to figure out the // calendar's timezone. list($calendarPath) = URLUtil::splitPath($uri); if (!isset($timeZones[$calendarPath])) { // Checking the calendar-timezone property. $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; $tzResult = $this->server->getProperties($calendarPath, [$tzProp]); if (isset($tzResult[$tzProp])) { // This property contains a VCALENDAR with a single // VTIMEZONE. $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]); $timeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); } else { // Defaulting to UTC. $timeZone = new DateTimeZone('UTC'); } $timeZones[$calendarPath] = $timeZone; } $vObject->expand($start, $end, $timeZones[$calendarPath]); } if ($needsJson) { $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); } else { $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } } $propertyList[]=$objProps; } $prefer = $this->server->getHTTPPRefer(); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal'])); } /** * 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 */ function calendarQueryReport($dom) { $parser = new CalendarQueryParser($dom); $parser->parse(); $path = $this->server->getRequestUri(); // TODO: move this into CalendarQueryParser $xpath = new \DOMXPath($dom); $xpath->registerNameSpace('cal',Plugin::NS_CALDAV); $xpath->registerNameSpace('dav','urn:DAV'); $needsJson = $xpath->evaluate("boolean(/cal:calendar-query/dav:prop/cal:calendar-data[@content-type='application/calendar+json'])"); $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); $depth = $this->server->getHTTPDepth(0); // The default result is an empty array $result = []; $calendarTimeZone = null; if ($parser->expand) { // We're expanding, and for that we need to figure out the // calendar's timezone. $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; $tzResult = $this->server->getProperties($path, [$tzProp]); if (isset($tzResult[$tzProp])) { // This property contains a VCALENDAR with a single // VTIMEZONE. $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]); $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); unset($vtimezoneObj); } else { // Defaulting to UTC. $calendarTimeZone = new DateTimeZone('UTC'); } } // The calendarobject was requested directly. In this case we handle // this locally. if ($depth == 0 && $node instanceof 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( $path, $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 CalendarQueryValidator(); $vObject = 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'], $calendarTimeZone); } if ($needsJson) { $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); } elseif ($parser->expand) { $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } } $result = [$properties]; } } } if ($node instanceof ICalendarObjectContainer && $depth === 0) { if(strpos($this->server->httpRequest->getHeader('User-Agent'), 'MSFT-WP/') === 0) { // Windows phone incorrectly supplied depth as 0, when it actually // should have set depth to 1. We're implementing a workaround here // to deal with this. $depth = 1; } else { throw new BadRequest('A calendar-query REPORT on a calendar with a Depth: 0 is undefined. Set Depth to 1'); } } // If we're dealing with a calendar, the calendar itself is responsible // for the calendar-query. if ($node instanceof ICalendarObjectContainer && $depth == 1) { $nodePaths = $node->calendarQuery($parser->filters); $timeZones = []; foreach($nodePaths as $path) { list($properties) = $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties); if (($needsJson || $parser->expand)) { $vObject = VObject\Reader::read($properties[200]['{' . self::NS_CALDAV . '}calendar-data']); if ($parser->expand) { $vObject->expand($parser->expand['start'], $parser->expand['end'], $calendarTimeZone); } if ($needsJson) { $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); } else { $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } } $result[] = $properties; } } $prefer = $this->server->getHTTPPRefer(); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); } /** * 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 = DAV\XMLUtil::toClarkNotation($childNode); if ($clark == '{' . self::NS_CALDAV . '}time-range') { $start = $childNode->getAttribute('start'); $end = $childNode->getAttribute('end'); break; } } if ($start) { $start = VObject\DateTimeParser::parseDateTime($start); } if ($end) { $end = VObject\DateTimeParser::parseDateTime($end); } $uri = $this->server->getRequestUri(); if (!$start && !$end) { throw new DAV\Exception\BadRequest('The freebusy report must have a time-range filter'); } $acl = $this->server->getPlugin('acl'); if ($acl) { $acl->checkPrivileges($uri,'{' . self::NS_CALDAV . '}read-free-busy'); } $calendar = $this->server->tree->getNodeForPath($uri); if (!$calendar instanceof ICalendar) { throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars'); } $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; // Figuring out the default timezone for the calendar, for floating // times. $calendarProps = $this->server->getProperties($uri, [$tzProp]); if (isset($calendarProps[$tzProp])) { $vtimezoneObj = VObject\Reader::read($calendarProps[$tzProp]); $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); } else { $calendarTimeZone = new DateTimeZone('UTC'); } // Doing a calendar-query first, to make sure we get the most // performance. $urls = $calendar->calendarQuery([ 'name' => 'VCALENDAR', 'comp-filters' => [ [ 'name' => 'VEVENT', 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => [ 'start' => $start, 'end' => $end, ], ], ], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => null, ]); $objects = array_map(function($url) use ($calendar) { $obj = $calendar->getChild($url)->get(); return $obj; }, $urls); $generator = new VObject\FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $generator->setTimeZone($calendarTimeZone); $result = $generator->getResult(); $result = $result->serialize(); $this->server->httpResponse->setStatus(200); $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->setHeader('Content-Length', strlen($result)); $this->server->httpResponse->setBody($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 DAV\IFile $node * @param resource $data * @param bool $modified Should be set to true, if this event handler * changed &$data. * @return void */ function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) { if (!$node instanceof ICalendarObject) return; // We're onyl interested in ICalendarObject nodes that are inside of a // real calendar. This is to avoid triggering validation and scheduling // for non-calendars (such as an inbox). list($parent) = URLUtil::splitPath($path); $parentNode = $this->server->tree->getNodeForPath($parent); if (!$parentNode instanceof ICalendar) return; $this->validateICalendar( $data, $path, $modified, $this->server->httpRequest, $this->server->httpResponse, false ); } /** * 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 DAV\ICollection $parentNode * @param bool $modified Should be set to true, if this event handler * changed &$data. * @return void */ function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) { if (!$parentNode instanceof ICalendar) return; $this->validateICalendar( $data, $path, $modified, $this->server->httpRequest, $this->server->httpResponse, true ); } /** * Checks if the submitted iCalendar data is in fact, valid. * * An exception is thrown if it's not. * * @param resource|string $data * @param string $path * @param bool $modified Should be set to true, if this event handler * changed &$data. * @param RequestInterface $request The http request. * @param ResponseInterface $response The http response. * @param bool $isNew Is the item a new one, or an update. * @return void */ protected function validateICalendar(&$data, $path, &$modified, RequestInterface $request, ResponseInterface $response, $isNew) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } $before = md5($data); // Converting the data to unicode, if needed. $data = DAV\StringUtil::ensureUTF8($data); if ($before!==md5($data)) $modified = true; try { // If the data starts with a [, we can reasonably assume we're dealing // with a jCal object. if (substr($data,0,1)==='[') { $vobj = VObject\Reader::readJson($data); // Converting $data back to iCalendar, as that's what we // technically support everywhere. $data = $vobj->serialize(); $modified = true; } else { $vobj = VObject\Reader::read($data); } } catch (VObject\ParseException $e) { throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); } if ($vobj->name !== 'VCALENDAR') { throw new DAV\Exception\UnsupportedMediaType('This collection can only support iCalendar objects.'); } $sCCS = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; // Get the Supported Components for the target calendar list($parentPath) = URLUtil::splitPath($path); $calendarProperties = $this->server->getProperties($parentPath, [$sCCS]); if (isset($calendarProperties[$sCCS])) { $supportedComponents = $calendarProperties[$sCCS]->getValue(); } else { $supportedComponents = ['VJOURNAL', 'VTODO', 'VEVENT']; } $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 (!in_array($foundType, $supportedComponents)) { throw new Exception\InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType); } if (!isset($component->UID)) { throw new DAV\Exception\BadRequest('Every ' . $component->name . ' component must have an UID'); } $foundUID = (string)$component->UID; } else { if ($foundType !== $component->name) { throw new 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 DAV\Exception\BadRequest('Every ' . $component->name . ' in this object must have identical UIDs'); } } break; default : throw new DAV\Exception\BadRequest('You are not allowed to create components of type: ' . $component->name . ' here'); } } if (!$foundType) throw new DAV\Exception\BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL'); // We use an extra variable to allow event handles to tell us wether // the object was modified or not. // // This helps us determine if we need to re-serialize the object. $subModified = false; $this->server->emit( 'calendarObjectChange', [ $request, $response, $vobj, $parentPath, &$subModified, $isNew ] ); if ($subModified) { // An event handler told us that it modified the object. $data = $vobj->serialize(); // Using md5 to figure out if there was an *actual* change. if (!$modified && $before !== md5($data)) { $modified = true; } } } /** * This method is used to generate HTML output for the * DAV\Browser\Plugin. This allows us to generate an interface users * can use to create new calendars. * * @param DAV\INode $node * @param string $output * @return bool */ function htmlActionsPanel(DAV\INode $node, &$output) { if (!$node instanceof CalendarHome) return; $output.= '

Create new calendar



'; 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 */ function browserPostAction($uri, $action, array $postVars) { if ($action!=='mkcalendar') return; $resourceType = ['{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar']; $properties = []; if (isset($postVars['{DAV:}displayname'])) { $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; } $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); return false; } /** * This event is triggered after GET requests. * * This is used to transform data into jCal, if this was requested. * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function httpAfterGet(RequestInterface $request, ResponseInterface $response) { if (strpos($response->getHeader('Content-Type'),'text/calendar')===false) { return; } $result = HTTP\Util::negotiate( $request->getHeader('Accept'), ['text/calendar', 'application/calendar+json'] ); if ($result !== 'application/calendar+json') { // Do nothing return; } // Transforming. $vobj = VObject\Reader::read($response->getBody()); $jsonBody = json_encode($vobj->jsonSerialize()); $response->setBody($jsonBody); $response->setHeader('Content-Type', 'application/calendar+json'); $response->setHeader('Content-Length', strlen($jsonBody)); } } sabre-dav-2.1.10/lib/CalDAV/Principal/000077500000000000000000000000001267035675400171775ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CalDAV/Principal/Collection.php000066400000000000000000000014751267035675400220120ustar00rootroot00000000000000principalBackend, $principalInfo); } } sabre-dav-2.1.10/lib/CalDAV/Principal/IProxyRead.php000066400000000000000000000006471267035675400217450ustar00rootroot00000000000000principalInfo = $principalInfo; $this->principalBackend = $principalBackend; } /** * Returns this principals name. * * @return string */ function getName() { return 'calendar-proxy-read'; } /** * Returns the last modification time * * @return null */ function getLastModified() { return null; } /** * Deletes the current node * * @throws DAV\Exception\Forbidden * @return void */ function delete() { throw new DAV\Exception\Forbidden('Permission denied to delete node'); } /** * Renames the node * * @throws DAV\Exception\Forbidden * @param string $name The new name * @return void */ function setName($name) { throw new 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 */ function getAlternateUriSet() { return []; } /** * Returns the full principal url * * @return string */ 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 */ 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 */ 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 */ 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 */ function getDisplayName() { return $this->getName(); } } sabre-dav-2.1.10/lib/CalDAV/Principal/ProxyWrite.php000066400000000000000000000075031267035675400220510ustar00rootroot00000000000000principalInfo = $principalInfo; $this->principalBackend = $principalBackend; } /** * Returns this principals name. * * @return string */ function getName() { return 'calendar-proxy-write'; } /** * Returns the last modification time * * @return null */ function getLastModified() { return null; } /** * Deletes the current node * * @throws DAV\Exception\Forbidden * @return void */ function delete() { throw new DAV\Exception\Forbidden('Permission denied to delete node'); } /** * Renames the node * * @throws DAV\Exception\Forbidden * @param string $name The new name * @return void */ function setName($name) { throw new 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 */ function getAlternateUriSet() { return array(); } /** * Returns the full principal url * * @return string */ 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 */ 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 */ 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 */ 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 */ function getDisplayName() { return $this->getName(); } } sabre-dav-2.1.10/lib/CalDAV/Principal/User.php000066400000000000000000000073031267035675400206310ustar00rootroot00000000000000principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name); if (!$principal) { throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); } if ($name === 'calendar-proxy-read') return new ProxyRead($this->principalBackend, $this->principalProperties); if ($name === 'calendar-proxy-write') return new ProxyWrite($this->principalBackend, $this->principalProperties); throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); } /** * Returns an array with all the child nodes * * @return DAV\INode[] */ function getChildren() { $r = []; if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) { $r[] = new ProxyRead($this->principalBackend, $this->principalProperties); } if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-write')) { $r[] = new ProxyWrite($this->principalBackend, $this->principalProperties); } return $r; } /** * Returns whether or not the child node exists * * @param string $name * @return bool */ function childExists($name) { try { $this->getChild($name); return true; } catch (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 */ function getACL() { $acl = parent::getACL(); $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', 'protected' => true, ]; $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', 'protected' => true, ]; return $acl; } } sabre-dav-2.1.10/lib/CalDAV/Property/000077500000000000000000000000001267035675400171025ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CalDAV/Property/AllowedSharingModes.php000066400000000000000000000035341267035675400235130ustar00rootroot00000000000000canBeShared = $canBeShared; $this->canBePublished = $canBePublished; } /** * Serializes the property in a DOMDocument * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server, \DOMElement $node) { $doc = $node->ownerDocument; if ($this->canBeShared) { $xcomp = $doc->createElement('cs:can-be-shared'); $node->appendChild($xcomp); } if ($this->canBePublished) { $xcomp = $doc->createElement('cs:can-be-published'); $node->appendChild($xcomp); } } } sabre-dav-2.1.10/lib/CalDAV/Property/EmailAddressSet.php000066400000000000000000000027171267035675400226330ustar00rootroot00000000000000emails = $emails; } /** * Returns the email addresses * * @return array */ function getValue() { return $this->emails; } /** * Serializes this property. * * It will additionally prepend the href property with the server's base uri. * * @param DAV\Server $server * @param \DOMElement $dom * @return void */ function serialize(DAV\Server $server,\DOMElement $dom) { $prefix = $server->xmlNamespaces['http://calendarserver.org/ns/']; foreach($this->emails as $email) { $elem = $dom->ownerDocument->createElement($prefix . ':email-address'); $elem->appendChild($dom->ownerDocument->createTextNode($email)); $dom->appendChild($elem); } } } sabre-dav-2.1.10/lib/CalDAV/Property/Invite.php000066400000000000000000000170141267035675400210540ustar00rootroot00000000000000users = $users; $this->organizer = $organizer; } /** * Returns the list of users, as it was passed to the constructor. * * @return array */ function getValue() { return $this->users; } /** * Serializes the property in a DOMDocument * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server,\DOMElement $node) { $doc = $node->ownerDocument; if (!is_null($this->organizer)) { $xorganizer = $doc->createElement('cs:organizer'); $href = $doc->createElement('d:href'); $href->appendChild($doc->createTextNode($this->organizer['href'])); $xorganizer->appendChild($href); if (isset($this->organizer['commonName']) && $this->organizer['commonName']) { $commonName = $doc->createElement('cs:common-name'); $commonName->appendChild($doc->createTextNode($this->organizer['commonName'])); $xorganizer->appendChild($commonName); } if (isset($this->organizer['firstName']) && $this->organizer['firstName']) { $firstName = $doc->createElement('cs:first-name'); $firstName->appendChild($doc->createTextNode($this->organizer['firstName'])); $xorganizer->appendChild($firstName); } if (isset($this->organizer['lastName']) && $this->organizer['lastName']) { $lastName = $doc->createElement('cs:last-name'); $lastName->appendChild($doc->createTextNode($this->organizer['lastName'])); $xorganizer->appendChild($lastName); } $node->appendChild($xorganizer); } foreach($this->users as $user) { $xuser = $doc->createElement('cs:user'); $href = $doc->createElement('d:href'); $href->appendChild($doc->createTextNode($user['href'])); $xuser->appendChild($href); if (isset($user['commonName']) && $user['commonName']) { $commonName = $doc->createElement('cs:common-name'); $commonName->appendChild($doc->createTextNode($user['commonName'])); $xuser->appendChild($commonName); } switch($user['status']) { case SharingPlugin::STATUS_ACCEPTED : $status = $doc->createElement('cs:invite-accepted'); $xuser->appendChild($status); break; case SharingPlugin::STATUS_DECLINED : $status = $doc->createElement('cs:invite-declined'); $xuser->appendChild($status); break; case SharingPlugin::STATUS_NORESPONSE : $status = $doc->createElement('cs:invite-noresponse'); $xuser->appendChild($status); break; case SharingPlugin::STATUS_INVALID : $status = $doc->createElement('cs:invite-invalid'); $xuser->appendChild($status); break; } $xaccess = $doc->createElement('cs:access'); if ($user['readOnly']) { $xaccess->appendChild( $doc->createElement('cs:read') ); } else { $xaccess->appendChild( $doc->createElement('cs:read-write') ); } $xuser->appendChild($xaccess); if (isset($user['summary']) && $user['summary']) { $summary = $doc->createElement('cs:summary'); $summary->appendChild($doc->createTextNode($user['summary'])); $xuser->appendChild($summary); } $node->appendChild($xuser); } } /** * Unserializes the property. * * This static method should return a an instance of this object. * * @param \DOMElement $prop * @param array $propertyMap * @return DAV\IProperty */ static function unserialize(\DOMElement $prop, array $propertyMap) { $xpath = new \DOMXPath($prop->ownerDocument); $xpath->registerNamespace('cs', CalDAV\Plugin::NS_CALENDARSERVER); $xpath->registerNamespace('d', 'urn:DAV'); $users = array(); foreach($xpath->query('cs:user', $prop) as $user) { $status = null; if ($xpath->evaluate('boolean(cs:invite-accepted)', $user)) { $status = SharingPlugin::STATUS_ACCEPTED; } elseif ($xpath->evaluate('boolean(cs:invite-declined)', $user)) { $status = SharingPlugin::STATUS_DECLINED; } elseif ($xpath->evaluate('boolean(cs:invite-noresponse)', $user)) { $status = SharingPlugin::STATUS_NORESPONSE; } elseif ($xpath->evaluate('boolean(cs:invite-invalid)', $user)) { $status = SharingPlugin::STATUS_INVALID; } else { throw new DAV\Exception('Every cs:user property must have one of cs:invite-accepted, cs:invite-declined, cs:invite-noresponse or cs:invite-invalid'); } $users[] = array( 'href' => $xpath->evaluate('string(d:href)', $user), 'commonName' => $xpath->evaluate('string(cs:common-name)', $user), 'readOnly' => $xpath->evaluate('boolean(cs:access/cs:read)', $user), 'summary' => $xpath->evaluate('string(cs:summary)', $user), 'status' => $status, ); } return new self($users); } } sabre-dav-2.1.10/lib/CalDAV/Property/ScheduleCalendarTransp.php000066400000000000000000000051671267035675400242020ustar00rootroot00000000000000value = $value; } /** * Returns the current value * * @return string */ function getValue() { return $this->value; } /** * Serializes the property in a DOMDocument * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server,\DOMElement $node) { $doc = $node->ownerDocument; switch($this->value) { case self::TRANSPARENT : $xval = $doc->createElement('cal:transparent'); break; case self::OPAQUE : $xval = $doc->createElement('cal:opaque'); break; } $node->appendChild($xval); } /** * Unserializes the DOMElement back into a Property class. * * @param \DOMElement $node * @param array $propertyMap * @return ScheduleCalendarTransp */ static function unserialize(\DOMElement $node, array $propertyMap) { $value = null; foreach($node->childNodes as $childNode) { switch(DAV\XMLUtil::toClarkNotation($childNode)) { case '{' . CalDAV\Plugin::NS_CALDAV . '}opaque' : $value = self::OPAQUE; break; case '{' . CalDAV\Plugin::NS_CALDAV . '}transparent' : $value = self::TRANSPARENT; break; } } if (is_null($value)) return null; return new self($value); } } sabre-dav-2.1.10/lib/CalDAV/Property/SupportedCalendarComponentSet.php000066400000000000000000000040321267035675400255700ustar00rootroot00000000000000components = $components; } /** * Returns the list of supported components * * @return array */ function getValue() { return $this->components; } /** * Serializes the property in a DOMDocument * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(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 * @param array $propertyMap * @return Property_SupportedCalendarComponentSet */ static function unserialize(\DOMElement $node, array $propertyMap) { $components = array(); foreach($node->childNodes as $childNode) { if (DAV\XMLUtil::toClarkNotation($childNode)==='{' . CalDAV\Plugin::NS_CALDAV . '}comp') { $components[] = $childNode->getAttribute('name'); } } return new self($components); } } sabre-dav-2.1.10/lib/CalDAV/Property/SupportedCalendarData.php000066400000000000000000000024551267035675400240320ustar00rootroot00000000000000ownerDocument; $prefix = isset($server->xmlNamespaces[Plugin::NS_CALDAV])?$server->xmlNamespaces[Plugin::NS_CALDAV]:'cal'; $caldata = $doc->createElement($prefix . ':calendar-data'); $caldata->setAttribute('content-type','text/calendar'); $caldata->setAttribute('version','2.0'); $node->appendChild($caldata); $caldata = $doc->createElement($prefix . ':calendar-data'); $caldata->setAttribute('content-type','application/calendar+json'); $node->appendChild($caldata); } } sabre-dav-2.1.10/lib/CalDAV/Property/SupportedCollationSet.php000066400000000000000000000022221267035675400241170ustar00rootroot00000000000000ownerDocument; $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') ); } } sabre-dav-2.1.10/lib/CalDAV/Schedule/000077500000000000000000000000001267035675400170125ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CalDAV/Schedule/IInbox.php000066400000000000000000000006371267035675400207210ustar00rootroot00000000000000senderEmail = $senderEmail; } /* * This initializes the plugin. * * This function is called by Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param Server $server * @return void */ function initialize(DAV\Server $server) { $server->on('schedule', [$this, 'schedule'], 120); } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using \Sabre\DAV\Server::getPlugin * * @return string */ function getPluginName() { return 'imip'; } /** * Event handler for the 'schedule' event. * * @param ITip\Message $iTipMessage * @return void */ function schedule(ITip\Message $iTipMessage) { // Not sending any emails if the system considers the update // insignificant. if (!$iTipMessage->significantChange) { if (!$iTipMessage->scheduleStatus) { $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; } return; } $summary = $iTipMessage->message->VEVENT->SUMMARY; if (parse_url($iTipMessage->sender, PHP_URL_SCHEME)!=='mailto') return; if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME)!=='mailto') return; $sender = substr($iTipMessage->sender,7); $recipient = substr($iTipMessage->recipient,7); if ($iTipMessage->senderName) { $sender = $iTipMessage->senderName . ' <' . $sender . '>'; } if ($iTipMessage->recipientName) { $recipient = $iTipMessage->recipientName . ' <' . $recipient . '>'; } $subject = 'SabreDAV iTIP message'; switch(strtoupper($iTipMessage->method)) { case 'REPLY' : $subject = 'Re: ' . $summary; break; case 'REQUEST' : $subject = $summary; break; case 'CANCEL' : $subject = 'Cancelled: ' . $summary; break; } $headers = [ 'Reply-To: ' . $sender, 'From: ' . $this->senderEmail, 'Content-Type: text/calendar; charset=UTF-8; method=' . $iTipMessage->method, ]; if (DAV\Server::$exposeVersion) { $headers[] = 'X-Sabre-Version: ' . DAV\Version::VERSION; } $this->mail( $recipient, $subject, $iTipMessage->message->serialize(), $headers ); $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip'; } // @codeCoverageIgnoreStart // This is deemed untestable in a reasonable manner /** * 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)); } // @codeCoverageIgnoreEnd } sabre-dav-2.1.10/lib/CalDAV/Schedule/IOutbox.php000066400000000000000000000005731267035675400211210ustar00rootroot00000000000000caldavBackend = $caldavBackend; $this->principalUri = $principalUri; } /** * Returns the name of the node. * * This is used to generate the url. * * @return string */ function getName() { return 'inbox'; } /** * Returns an array with all the child nodes * * @return \Sabre\DAV\INode[] */ function getChildren() { $objs = $this->caldavBackend->getSchedulingObjects($this->principalUri); $children = []; foreach($objs as $obj) { //$obj['acl'] = $this->getACL(); $obj['principaluri'] = $this->principalUri; $children[] = new SchedulingObject($this->caldavBackend,$obj); } return $children; } /** * Creates a new file in the directory * * Data will either be supplied as a stream resource, or in certain cases * as a string. Keep in mind that you may have to support either. * * After succesful creation of the file, you may choose to return the ETag * of the new file here. * * The returned ETag must be surrounded by double-quotes (The quotes should * be part of the actual string). * * If you cannot accurately determine the ETag, you should not return it. * If you don't store the file exactly as-is (you're transforming it * somehow) you should also not return an ETag. * * This means that if a subsequent GET to this new file does not exactly * return the same contents of what was submitted here, you are strongly * recommended to omit the ETag. * * @param string $name Name of the file * @param resource|string $data Initial payload * @return null|string */ function createFile($name, $data = null) { $this->caldavBackend->createSchedulingObject($this->principalUri, $name, $data); } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ 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 */ 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 */ function getACL() { return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}write-properties', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}unbind', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-read', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}unbind', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver-invite', 'principal' => '{DAV:}authenticated', 'protected' => true, ], [ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver-reply', 'principal' => '{DAV:}authenticated', 'protected' => true, ], ]; } /** * Updates the ACL * * This method will receive a list of new ACE's. * * @param array $acl * @return void */ function setACL(array $acl) { throw new 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 */ function getSupportedPrivilegeSet() { $ns = '{' . CalDAV\Plugin::NS_CALDAV . '}'; $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet(); $default['aggregates'][] = [ 'privilege' => $ns . 'schedule-deliver', 'aggregates' => [ ['privilege' => $ns . 'schedule-deliver-invite'], ['privilege' => $ns . 'schedule-deliver-reply'], ], ]; 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 */ function calendarQuery(array $filters) { $result = []; $validator = new CalDAV\CalendarQueryValidator(); $objects = $this->caldavBackend->getSchedulingObjects($this->principalUri); foreach($objects as $object) { $vObject = VObject\Reader::read($object['calendardata']); if ($validator->validate($vObject, $filters)) { $result[] = $object['uri']; } } return $result; } } sabre-dav-2.1.10/lib/CalDAV/Schedule/Outbox.php000066400000000000000000000111051267035675400210010ustar00rootroot00000000000000principalUri = $principalUri; } /** * Returns the name of the node. * * This is used to generate the url. * * @return string */ function getName() { return 'outbox'; } /** * Returns an array with all the child nodes * * @return \Sabre\DAV\INode[] */ function getChildren() { return []; } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ 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 */ 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 */ function getACL() { return [ [ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-read', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], ]; } /** * Updates the ACL * * This method will receive a list of new ACE's. * * @param array $acl * @return void */ function setACL(array $acl) { throw new 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 */ function getSupportedPrivilegeSet() { $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet(); $default['aggregates'][] = [ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', ]; $default['aggregates'][] = [ 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', ]; return $default; } } sabre-dav-2.1.10/lib/CalDAV/Schedule/Plugin.php000066400000000000000000000726761267035675400210030ustar00rootroot00000000000000server = $server; $server->on('method:POST', [$this, 'httpPost']); $server->on('propFind', [$this, 'propFind']); $server->on('calendarObjectChange', [$this, 'calendarObjectChange']); $server->on('beforeUnbind', [$this, 'beforeUnbind']); $server->on('schedule', [$this, 'scheduleLocalDelivery']); $ns = '{' . self::NS_CALDAV . '}'; /** * This information ensures that the {DAV:}resourcetype property has * the correct values. */ $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IOutbox'] = $ns . 'schedule-outbox'; $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IInbox'] = $ns . 'schedule-inbox'; /** * Properties we protect are made read-only by the server. */ array_push($server->protectedProperties, $ns . 'schedule-inbox-URL', $ns . 'schedule-outbox-URL', $ns . 'calendar-user-address-set', $ns . 'calendar-user-type', $ns . 'schedule-default-calendar-URL' ); } /** * 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 */ function getHTTPMethods($uri) { try { $node = $this->server->tree->getNodeForPath($uri); } catch (NotFound $e) { return []; } if ($node instanceof IOutbox) { return ['POST']; } return []; } /** * This method handles POST request for the outbox. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpPost(RequestInterface $request, ResponseInterface $response) { // Checking if this is a text/calendar content type $contentType = $request->getHeader('Content-Type'); if (strpos($contentType, 'text/calendar')!==0) { return; } $path = $request->getPath(); // Checking if we're talking to an outbox try { $node = $this->server->tree->getNodeForPath($path); } catch (NotFound $e) { return; } if (!$node instanceof IOutbox) return; $this->server->transactionType = 'post-caldav-outbox'; $this->outboxRequest($node, $request, $response); // Returning false breaks the event chain and tells the server we've // handled the request. return false; } /** * This method handler is invoked during fetching of properties. * * We use this event to add calendar-auto-schedule-specific properties. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { if (!$node instanceof DAVACL\IPrincipal) return; $caldavPlugin = $this->server->getPlugin('caldav'); $principalUrl = $node->getPrincipalUrl(); // schedule-outbox-URL property $propFind->handle('{' . self::NS_CALDAV . '}schedule-outbox-URL' , function() use ($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $outboxPath = $calendarHomePath . '/outbox/'; return new Href($outboxPath); }); // schedule-inbox-URL property $propFind->handle('{' . self::NS_CALDAV . '}schedule-inbox-URL' , function() use ($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $inboxPath = $calendarHomePath . '/inbox/'; return new Href($inboxPath); }); $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function() use ($principalUrl, $caldavPlugin) { // We don't support customizing this property yet, so in the // meantime we just grab the first calendar in the home-set. $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); $sccs = '{' . self::NS_CALDAV . '}supported-calendar-component-set'; $result = $this->server->getPropertiesForPath($calendarHomePath, [ '{DAV:}resourcetype', $sccs, ], 1); foreach($result as $child) { if (!isset($child[200]['{DAV:}resourcetype']) || !$child[200]['{DAV:}resourcetype']->is('{' . self::NS_CALDAV . '}calendar') || $child[200]['{DAV:}resourcetype']->is('{http://calendarserver.org/ns/}shared')) { // Node is either not a calendar or a shared instance. continue; } if (!isset($child[200][$sccs]) || in_array('VEVENT', $child[200][$sccs]->getValue())) { // Either there is no supported-calendar-component-set // (which is fine) or we found one that supports VEVENT. return new Href($child['href']); } } }); // The server currently reports every principal to be of type // 'INDIVIDUAL' $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function() { return 'INDIVIDUAL'; }); } /** * This method is triggered whenever there was a calendar object gets * created or updated. * * @param RequestInterface $request HTTP request * @param ResponseInterface $response HTTP Response * @param VCalendar $vCal Parsed iCalendar object * @param mixed $calendarPath Path to calendar collection * @param mixed $modified The iCalendar object has been touched. * @param mixed $isNew Whether this was a new item or we're updating one * @return void */ function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) { if (!$this->scheduleReply($this->server->httpRequest)) { return; } $calendarNode = $this->server->tree->getNodeForPath($calendarPath); $addresses = $this->getAddressesForPrincipal( $calendarNode->getOwner() ); $broker = new ITip\Broker(); if (!$isNew) { $node = $this->server->tree->getNodeForPath($request->getPath()); $oldObj = Reader::read($node->get()); } else { $oldObj = null; } $this->processICalendarChange($oldObj, $vCal, $addresses, [], $modified); } /** * This method is responsible for delivering the ITip message. * * @param ITip\Message $itipMessage * @return void */ function deliver(ITip\Message $iTipMessage) { $this->server->emit('schedule', [$iTipMessage]); if (!$iTipMessage->scheduleStatus) { $iTipMessage->scheduleStatus='5.2;There was no system capable of delivering the scheduling message'; } // In case the change was considered 'insignificant', we are going to // remove any error statuses, if any. See ticket #525. list($baseCode) = explode('.', $iTipMessage->scheduleStatus); if (!$iTipMessage->significantChange && in_array($baseCode,['3','5'])) { $iTipMessage->scheduleStatus = null; } } /** * This method is triggered before a file gets deleted. * * We use this event to make sure that when this happens, attendees get * cancellations, and organizers get 'DECLINED' statuses. * * @param string $path * @return void */ function beforeUnbind($path) { // FIXME: We shouldn't trigger this functionality when we're issuing a // MOVE. This is a hack. if ($this->server->httpRequest->getMethod()==='MOVE') return; $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof ICalendarObject || $node instanceof ISchedulingObject) { return; } if (!$this->scheduleReply($this->server->httpRequest)) { return; } $addresses = $this->getAddressesForPrincipal( $node->getOwner() ); $broker = new ITip\Broker(); $messages = $broker->parseEvent(null, $addresses, $node->get()); foreach($messages as $message) { $this->deliver($message); } } /** * Event handler for the 'schedule' event. * * This handler attempts to look at local accounts to deliver the * scheduling object. * * @param ITip\Message $iTipMessage * @return void */ function scheduleLocalDelivery(ITip\Message $iTipMessage) { $aclPlugin = $this->server->getPlugin('acl'); // Local delivery is not available if the ACL plugin is not loaded. if (!$aclPlugin) { return; } $caldavNS = '{' . Plugin::NS_CALDAV . '}'; $principalUri = $aclPlugin->getPrincipalByUri($iTipMessage->recipient); if (!$principalUri) { $iTipMessage->scheduleStatus = '3.7;Could not find principal.'; return; } // We found a principal URL, now we need to find its inbox. // Unfortunately we may not have sufficient privileges to find this, so // we are temporarily turning off ACL to let this come through. // // Once we support PHP 5.5, this should be wrapped in a try..finally // block so we can ensure that this privilege gets added again after. $this->server->removeListener('propFind', [$aclPlugin, 'propFind']); $result = $this->server->getProperties( $principalUri, [ '{DAV:}principal-URL', $caldavNS . 'calendar-home-set', $caldavNS . 'schedule-inbox-URL', $caldavNS . 'schedule-default-calendar-URL', '{http://sabredav.org/ns}email-address', ] ); // Re-registering the ACL event $this->server->on('propFind', [$aclPlugin, 'propFind'], 20); if (!isset($result[$caldavNS . 'schedule-inbox-URL'])) { $iTipMessage->scheduleStatus = '5.2;Could not find local inbox'; return; } if (!isset($result[$caldavNS . 'calendar-home-set'])) { $iTipMessage->scheduleStatus = '5.2;Could not locate a calendar-home-set'; return; } if (!isset($result[$caldavNS . 'schedule-default-calendar-URL'])) { $iTipMessage->scheduleStatus = '5.2;Could not find a schedule-default-calendar-URL property'; return; } $calendarPath = $result[$caldavNS . 'schedule-default-calendar-URL']->getHref(); $homePath = $result[$caldavNS . 'calendar-home-set']->getHref(); $inboxPath = $result[$caldavNS . 'schedule-inbox-URL']->getHref(); if ($iTipMessage->method === 'REPLY') { $privilege = 'schedule-deliver-reply'; } else { $privilege = 'schedule-deliver-invite'; } if (!$aclPlugin->checkPrivileges($inboxPath, $caldavNS . $privilege, DAVACL\Plugin::R_PARENT, false)) { $iTipMessage->scheduleStatus = '3.8;organizer did not have the '.$privilege.' privilege on the attendees inbox'; return; } // Next, we're going to find out if the item already exits in one of // the users' calendars. $uid = $iTipMessage->uid; $newFileName = 'sabredav-' . \Sabre\DAV\UUIDUtil::getUUID() . '.ics'; $home = $this->server->tree->getNodeForPath($homePath); $inbox = $this->server->tree->getNodeForPath($inboxPath); $currentObject = null; $objectNode = null; $isNewNode = false; $result = $home->getCalendarObjectByUID($uid); if ($result) { // There was an existing object, we need to update probably. $objectPath = $homePath . '/' . $result; $objectNode = $this->server->tree->getNodeForPath($objectPath); $oldICalendarData = $objectNode->get(); $currentObject = Reader::read($oldICalendarData); } else { $isNewNode = true; } $broker = new ITip\Broker(); $newObject = $broker->processMessage($iTipMessage, $currentObject); $inbox->createFile($newFileName, $iTipMessage->message->serialize()); if (!$newObject) { // We received an iTip message referring to a UID that we don't // have in any calendars yet, and processMessage did not give us a // calendarobject back. // // The implication is that processMessage did not understand the // iTip message. $iTipMessage->scheduleStatus = '5.0;iTip message was not processed by the server, likely because we didn\'t understand it.'; return; } // Note that we are bypassing ACL on purpose by calling this directly. // We may need to look a bit deeper into this later. Supporting ACL // here would be nice. if ($isNewNode) { $calendar = $this->server->tree->getNodeForPath($calendarPath); $calendar->createFile($newFileName, $newObject->serialize()); } else { // If the message was a reply, we may have to inform other // attendees of this attendees status. Therefore we're shooting off // another itipMessage. if ($iTipMessage->method === 'REPLY') { $this->processICalendarChange( $oldICalendarData, $newObject, [$iTipMessage->recipient], [$iTipMessage->sender] ); } $objectNode->put($newObject->serialize()); } $iTipMessage->scheduleStatus = '1.2;Message delivered locally'; } /** * This method looks at an old iCalendar object, a new iCalendar object and * starts sending scheduling messages based on the changes. * * A list of addresses needs to be specified, so the system knows who made * the update, because the behavior may be different based on if it's an * attendee or an organizer. * * This method may update $newObject to add any status changes. * * @param VCalendar|string $oldObject * @param VCalendar $newObject * @param array $addresses * @param array $ignore Any addresses to not send messages to. * @param boolean $modified A marker to indicate that the original object * modified by this process. * @return void */ protected function processICalendarChange($oldObject = null, VCalendar $newObject, array $addresses, array $ignore = [], &$modified = false) { $broker = new ITip\Broker(); $messages = $broker->parseEvent($newObject, $addresses, $oldObject); if ($messages) $modified = true; foreach($messages as $message) { if (in_array($message->recipient, $ignore)) { continue; } $this->deliver($message); if (isset($newObject->VEVENT->ORGANIZER) && ($newObject->VEVENT->ORGANIZER->getNormalizedValue() === $message->recipient)) { if ($message->scheduleStatus) { $newObject->VEVENT->ORGANIZER['SCHEDULE-STATUS'] = $message->getScheduleStatus(); } unset($newObject->VEVENT->ORGANIZER['SCHEDULE-FORCE-SEND']); } else { if (isset($newObject->VEVENT->ATTENDEE)) foreach($newObject->VEVENT->ATTENDEE as $attendee) { if ($attendee->getNormalizedValue() === $message->recipient) { if ($message->scheduleStatus) { $attendee['SCHEDULE-STATUS'] = $message->getScheduleStatus(); } unset($attendee['SCHEDULE-FORCE-SEND']); break; } } } } } /** * Returns a list of addresses that are associated with a principal. * * @param string $principal * @return array */ protected function getAddressesForPrincipal($principal) { $CUAS = '{' . self::NS_CALDAV . '}calendar-user-address-set'; $properties = $this->server->getProperties( $principal, [$CUAS] ); // If we can't find this information, we'll stop processing if (!isset($properties[$CUAS])) { return; } $addresses = $properties[$CUAS]->getHrefs(); return $addresses; } /** * This method handles POST requests to the schedule-outbox. * * Currently, two types of requests are support: * * FREEBUSY requests from RFC 6638 * * Simple iTIP messages from draft-desruisseaux-caldav-sched-04 * * The latter is from an expired early draft of the CalDAV scheduling * extensions, but iCal depends on a feature from that spec, so we * implement it. * * @param IOutbox $outboxNode * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function outboxRequest(IOutbox $outboxNode, RequestInterface $request, ResponseInterface $response) { $outboxPath = $request->getPath(); // Parsing the request body try { $vObject = VObject\Reader::read($request->getBody()); } catch (VObject\ParseException $e) { throw new BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); } // The incoming iCalendar object must have a METHOD property, and a // component. The combination of both determines what type of request // this is. $componentType = null; foreach($vObject->getComponents() as $component) { if ($component->name !== 'VTIMEZONE') { $componentType = $component->name; break; } } if (is_null($componentType)) { throw new BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component'); } // Validating the METHOD $method = strtoupper((string)$vObject->METHOD); if (!$method) { throw new BadRequest('A METHOD property must be specified in iTIP messages'); } // So we support one type of request: // // REQUEST with a VFREEBUSY component $acl = $this->server->getPlugin('acl'); if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') { $acl && $acl->checkPrivileges($outboxPath, '{' . self::NS_CALDAV . '}schedule-query-freebusy'); $this->handleFreeBusyRequest($outboxNode, $vObject, $request, $response); } else { throw new NotImplemented('We only support VFREEBUSY (REQUEST) on this endpoint'); } } /** * This method is responsible for parsing a free-busy query request and * returning it's result. * * @param IOutbox $outbox * @param VObject\Component $vObject * @param RequestInterface $request * @param ResponseInterface $response * @return string */ protected function handleFreeBusyRequest(IOutbox $outbox, VObject\Component $vObject, RequestInterface $request, ResponseInterface $response) { $vFreeBusy = $vObject->VFREEBUSY; $organizer = $vFreeBusy->organizer; $organizer = (string)$organizer; // Validating if the organizer matches the owner of the inbox. $owner = $outbox->getOwner(); $caldavNS = '{' . self::NS_CALDAV . '}'; $uas = $caldavNS . 'calendar-user-address-set'; $props = $this->server->getProperties($owner, [$uas]); if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) { throw new Forbidden('The organizer in the request did not match any of the addresses for the owner of this inbox'); } if (!isset($vFreeBusy->ATTENDEE)) { throw new BadRequest('You must at least specify 1 attendee'); } $attendees = []; foreach($vFreeBusy->ATTENDEE as $attendee) { $attendees[]= (string)$attendee; } if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) { throw new BadRequest('DTSTART and DTEND must both be specified'); } $startRange = $vFreeBusy->DTSTART->getDateTime(); $endRange = $vFreeBusy->DTEND->getDateTime(); $results = []; foreach($attendees as $attendee) { $results[] = $this->getFreeBusyForEmail($attendee, $startRange, $endRange, $vObject); } $dom = new \DOMDocument('1.0','utf-8'); $dom->formatOutput = true; $scheduleResponse = $dom->createElement('cal:schedule-response'); foreach($this->server->xmlNamespaces as $namespace=>$prefix) { $scheduleResponse->setAttribute('xmlns:' . $prefix,$namespace); } $dom->appendChild($scheduleResponse); foreach($results as $result) { $xresponse = $dom->createElement('cal:response'); $recipient = $dom->createElement('cal:recipient'); $recipientHref = $dom->createElement('d:href'); $recipientHref->appendChild($dom->createTextNode($result['href'])); $recipient->appendChild($recipientHref); $xresponse->appendChild($recipient); $reqStatus = $dom->createElement('cal:request-status'); $reqStatus->appendChild($dom->createTextNode($result['request-status'])); $xresponse->appendChild($reqStatus); if (isset($result['calendar-data'])) { $calendardata = $dom->createElement('cal:calendar-data'); $calendardata->appendChild($dom->createTextNode(str_replace("\r\n","\n",$result['calendar-data']->serialize()))); $xresponse->appendChild($calendardata); } $scheduleResponse->appendChild($xresponse); } $response->setStatus(200); $response->setHeader('Content-Type','application/xml'); $response->setBody($dom->saveXML()); } /** * Returns free-busy information for a specific address. The returned * data is an array containing the following properties: * * calendar-data : A VFREEBUSY VObject * request-status : an iTip status code. * href: The principal's email address, as requested * * The following request status codes may be returned: * * 2.0;description * * 3.7;description * * @param string $email address * @param \DateTime $start * @param \DateTime $end * @param VObject\Component $request * @return array */ protected function getFreeBusyForEmail($email, \DateTime $start, \DateTime $end, VObject\Component $request) { $caldavNS = '{' . Plugin::NS_CALDAV . '}'; $aclPlugin = $this->server->getPlugin('acl'); if (substr($email,0,7)==='mailto:') $email = substr($email,7); $result = $aclPlugin->principalSearch( ['{http://sabredav.org/ns}email-address' => $email], [ '{DAV:}principal-URL', $caldavNS . 'calendar-home-set', '{http://sabredav.org/ns}email-address', ] ); if (!count($result)) { return [ 'request-status' => '3.7;Could not find principal', 'href' => 'mailto:' . $email, ]; } if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { return [ 'request-status' => '3.7;No calendar-home-set property found', 'href' => 'mailto:' . $email, ]; } $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); // Grabbing the calendar list $objects = []; $calendarTimeZone = new DateTimeZone('UTC'); foreach($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) { if (!$node instanceof ICalendar) { continue; } $sct = $caldavNS . 'schedule-calendar-transp'; $ctz = $caldavNS . 'calendar-timezone'; $props = $node->getProperties([$sct, $ctz]); if (isset($props[$sct]) && $props[$sct]->getValue() == ScheduleCalendarTransp::TRANSPARENT) { // If a calendar is marked as 'transparent', it means we must // ignore it for free-busy purposes. continue; } $aclPlugin->checkPrivileges($homeSet . $node->getName() ,$caldavNS . 'read-free-busy'); if (isset($props[$ctz])) { $vtimezoneObj = VObject\Reader::read($props[$ctz]); $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); } // Getting the list of object uris within the time-range $urls = $node->calendarQuery([ 'name' => 'VCALENDAR', 'comp-filters' => [ [ 'name' => 'VEVENT', 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => [ 'start' => $start, 'end' => $end, ], ], ], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => null, ]); $calObjects = array_map(function($url) use ($node) { $obj = $node->getChild($url)->get(); return $obj; }, $urls); $objects = array_merge($objects,$calObjects); } $vcalendar = new VObject\Component\VCalendar(); $vcalendar->METHOD = 'REPLY'; $generator = new VObject\FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $generator->setBaseObject($vcalendar); $generator->setTimeZone($calendarTimeZone); $result = $generator->getResult(); $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; $vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID; $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; return [ 'calendar-data' => $result, 'request-status' => '2.0;Success', 'href' => 'mailto:' . $email, ]; } /** * This method checks the 'Schedule-Reply' header * and returns false if it's 'F', otherwise true. * * @param RequestInterface $request * @return bool */ private function scheduleReply(RequestInterface $request) { $scheduleReply = $request->getHeader('Schedule-Reply'); return $scheduleReply!=='F'; } } sabre-dav-2.1.10/lib/CalDAV/Schedule/SchedulingObject.php000066400000000000000000000111241267035675400227360ustar00rootroot00000000000000caldavBackend = $caldavBackend; if (!isset($objectData['uri'])) { throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property'); } $this->objectData = $objectData; } /** * Returns the ICalendar-formatted object * * @return string */ 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->getSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']); } return $this->objectData['calendardata']; } /** * Updates the ICalendar-formatted object * * @param string|resource $calendarData * @return string */ function put($calendarData) { throw new MethodNotAllowed('Updating scheduling objects is not supported'); } /** * Deletes the scheduling message * * @return void */ function delete() { $this->caldavBackend->deleteSchedulingObject($this->objectData['principaluri'],$this->objectData['uri']); } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ function getOwner() { return $this->objectData['principaluri']; } /** * 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 */ function getACL() { // An alternative acl may be specified in the object data. // if (isset($this->objectData['acl'])) { return $this->objectData['acl']; } // The default ACL return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->objectData['principaluri'], 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->objectData['principaluri'], 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-read', 'protected' => true, ], ]; } } sabre-dav-2.1.10/lib/CalDAV/ShareableCalendar.php000066400000000000000000000037751267035675400213230ustar00rootroot00000000000000caldavBackend->updateShares($this->calendarInfo['id'], $add, $remove); } /** * Returns the list of people whom this calendar is shared with. * * Every element in this array should have the following properties: * * href - Often a mailto: address * * commonName - Optional, for example a first + last name * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. * * readOnly - boolean * * summary - Optional, a description for the share * * @return array */ function getShares() { return $this->caldavBackend->getShares($this->calendarInfo['id']); } /** * Marks this calendar as published. * * Publishing a calendar should automatically create a read-only, public, * subscribable calendar. * * @param bool $value * @return void */ function setPublishStatus($value) { $this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value); } } sabre-dav-2.1.10/lib/CalDAV/SharedCalendar.php000066400000000000000000000103001267035675400206210ustar00rootroot00000000000000calendarInfo['{http://calendarserver.org/ns/}shared-url']; } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ function getOwner() { return $this->calendarInfo['{http://sabredav.org/ns}owner-principal']; } /** * 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 */ function getACL() { // The top-level ACL only contains access information for the true // owner of the calendar, so we need to add the information for the // sharee. $acl = parent::getACL(); $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => $this->calendarInfo['principaluri'], 'protected' => true, ]; if ($this->calendarInfo['{http://sabredav.org/ns}read-only']) { $acl[] = [ 'privilege' => '{DAV:}write-properties', 'principal' => $this->calendarInfo['principaluri'], 'protected' => true, ]; } else { $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $this->calendarInfo['principaluri'], 'protected' => true, ]; } return $acl; } /** * This method returns the ACL's for calendar objects in this calendar. * The result of this method automatically gets passed to the * calendar-object nodes in the calendar. * * @return array */ function getChildACL() { $acl = parent::getChildACL(); $acl[] = [ 'privilege' => '{DAV:}read', 'principal' => $this->calendarInfo['principaluri'], 'protected' => true, ]; if (!$this->calendarInfo['{http://sabredav.org/ns}read-only']) { $acl[] = [ 'privilege' => '{DAV:}write', 'principal' => $this->calendarInfo['principaluri'], 'protected' => true, ]; } return $acl; } /** * Returns the list of people whom this calendar is shared with. * * Every element in this array should have the following properties: * * href - Often a mailto: address * * commonName - Optional, for example a first + last name * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. * * readOnly - boolean * * summary - Optional, a description for the share * * @return array */ function getShares() { return $this->caldavBackend->getShares($this->calendarInfo['id']); } } sabre-dav-2.1.10/lib/CalDAV/SharingPlugin.php000066400000000000000000000402261267035675400205450ustar00rootroot00000000000000server = $server; $server->resourceTypeMapping['Sabre\\CalDAV\\ISharedCalendar'] = '{' . Plugin::NS_CALENDARSERVER . '}shared'; array_push( $this->server->protectedProperties, '{' . Plugin::NS_CALENDARSERVER . '}invite', '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', '{' . Plugin::NS_CALENDARSERVER . '}shared-url' ); $this->server->on('propFind', [$this,'propFindEarly']); $this->server->on('propFind', [$this,'propFindLate'], 150); $this->server->on('propPatch', [$this, 'propPatch'], 40); $this->server->on('method:POST', [$this, 'httpPost']); } /** * This event is triggered when properties are requested for a certain * node. * * This allows us to inject any properties early. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) { if ($node instanceof IShareableCalendar) { $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}invite', function() use ($node) { return new Property\Invite( $node->getShares() ); }); } if ($node instanceof ISharedCalendar) { $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}shared-url', function() use ($node) { return new DAV\Property\Href( $node->getSharedUrl() ); }); $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}invite', function() use ($node) { // Fetching owner information $props = $this->server->getPropertiesForPath($node->getOwner(), [ '{http://sabredav.org/ns}email-address', '{DAV:}displayname', ], 0); $ownerInfo = [ 'href' => $node->getOwner(), ]; if (isset($props[0][200])) { // We're mapping the internal webdav properties to the // elements caldav-sharing expects. if (isset($props[0][200]['{http://sabredav.org/ns}email-address'])) { $ownerInfo['href'] = 'mailto:' . $props[0][200]['{http://sabredav.org/ns}email-address']; } if (isset($props[0][200]['{DAV:}displayname'])) { $ownerInfo['commonName'] = $props[0][200]['{DAV:}displayname']; } } return new Property\Invite( $node->getShares(), $ownerInfo ); }); } } /** * This method is triggered *after* all properties have been retrieved. * This allows us to inject the correct resourcetype for calendars that * have been shared. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFindLate(DAV\PropFind $propFind, DAV\INode $node) { if ($node instanceof IShareableCalendar) { if ($rt = $propFind->get('{DAV:}resourcetype')) { if (count($node->getShares()) > 0) { $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared-owner'); } } $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', function() { return new Property\AllowedSharingModes(true,false); }); } } /** * This method is trigged when a user attempts to update a node's * properties. * * A previous draft of the sharing spec stated that it was possible to use * PROPPATCH to remove 'shared-owner' from the resourcetype, thus unsharing * the calendar. * * Even though this is no longer in the current spec, we keep this around * because OS X 10.7 may still make use of this feature. * * @param string $path * @param DAV\PropPatch $propPatch * @return void */ function propPatch($path, DAV\PropPatch $propPatch) { $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof IShareableCalendar) return; $propPatch->handle('{DAV:}resourcetype', function($value) use ($node) { if($value->is('{' . Plugin::NS_CALENDARSERVER . '}shared-owner')) return false; $shares = $node->getShares(); $remove = []; foreach($shares as $share) { $remove[] = $share['href']; } $node->updateShares([], $remove); return true; }); } /** * We intercept this to handle POST requests on calendars. * * @param RequestInterface $request * @param ResponseInterface $response * @return null|bool */ function httpPost(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Only handling xml $contentType = $request->getHeader('Content-Type'); if (strpos($contentType,'application/xml')===false && strpos($contentType,'text/xml')===false) return; // Making sure the node exists try { $node = $this->server->tree->getNodeForPath($path); } catch (DAV\Exception\NotFound $e) { return; } $requestBody = $request->getBodyAsString(); // If this request handler could not deal with this POST request, it // will return 'null' and other plugins get a chance to handle the // request. // // However, we already requested the full body. This is a problem, // because a body can only be read once. This is why we preemptively // re-populated the request body with the existing data. $request->setBody($requestBody); $dom = DAV\XMLUtil::loadDOMDocument($requestBody); $documentType = DAV\XMLUtil::toClarkNotation($dom->firstChild); switch($documentType) { // Dealing with the 'share' document, which modified invitees on a // calendar. case '{' . Plugin::NS_CALENDARSERVER . '}share' : // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } $this->server->transactionType = 'post-calendar-share'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $mutations = $this->parseShareRequest($dom); $node->updateShares($mutations[0], $mutations[1]); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; // The invite-reply document is sent when the user replies to an // invitation of a calendar share. case '{'. Plugin::NS_CALENDARSERVER.'}invite-reply' : // This only works on the calendar-home-root node. if (!$node instanceof CalendarHome) { return; } $this->server->transactionType = 'post-invite-reply'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $message = $this->parseInviteReplyRequest($dom); $url = $node->shareReply( $message['href'], $message['status'], $message['calendarUri'], $message['inReplyTo'], $message['summary'] ); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); if ($url) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $root = $dom->createElement('cs:shared-as'); foreach($this->server->xmlNamespaces as $namespace => $prefix) { $root->setAttribute('xmlns:' . $prefix, $namespace); } $dom->appendChild($root); $href = new DAV\Property\Href($url); $href->serialize($this->server, $root); $response->setHeader('Content-Type','application/xml'); $response->setBody($dom->saveXML()); } // Breaking the event chain return false; case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar' : // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } $this->server->transactionType = 'post-publish-calendar'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $node->setPublishStatus(true); // iCloud sends back the 202, so we will too. $response->setStatus(202); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar' : // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } $this->server->transactionType = 'post-unpublish-calendar'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $node->setPublishStatus(false); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; } } /** * Parses the 'share' POST request. * * This method returns an array, containing two arrays. * The first array is a list of new sharees. Every element is a struct * containing a: * * href element. (usually a mailto: address) * * commonName element (often a first and lastname, but can also be * false) * * readOnly (true or false) * * summary (A description of the share, can also be false) * * The second array is a list of sharees that are to be removed. This is * just a simple array with 'hrefs'. * * @param \DOMDocument $dom * @return array */ function parseShareRequest(\DOMDocument $dom) { $xpath = new \DOMXPath($dom); $xpath->registerNamespace('cs', Plugin::NS_CALENDARSERVER); $xpath->registerNamespace('d', 'urn:DAV'); $set = []; $elems = $xpath->query('cs:set'); for($i=0; $i < $elems->length; $i++) { $xset = $elems->item($i); $set[] = [ 'href' => $xpath->evaluate('string(d:href)', $xset), 'commonName' => $xpath->evaluate('string(cs:common-name)', $xset), 'summary' => $xpath->evaluate('string(cs:summary)', $xset), 'readOnly' => $xpath->evaluate('boolean(cs:read)', $xset)!==false ]; } $remove = []; $elems = $xpath->query('cs:remove'); for($i=0; $i < $elems->length; $i++) { $xremove = $elems->item($i); $remove[] = $xpath->evaluate('string(d:href)', $xremove); } return [$set, $remove]; } /** * Parses the 'invite-reply' POST request. * * This method returns an array, containing the following properties: * * href - The sharee who is replying * * status - One of the self::STATUS_* constants * * calendarUri - The url of the shared calendar * * inReplyTo - The unique id of the share invitation. * * summary - Optional description of the reply. * * @param \DOMDocument $dom * @return array */ function parseInviteReplyRequest(\DOMDocument $dom) { $xpath = new \DOMXPath($dom); $xpath->registerNamespace('cs', Plugin::NS_CALENDARSERVER); $xpath->registerNamespace('d', 'urn:DAV'); $hostHref = $xpath->evaluate('string(cs:hosturl/d:href)'); if (!$hostHref) { throw new DAV\Exception\BadRequest('The {' . Plugin::NS_CALENDARSERVER . '}hosturl/{DAV:}href element is required'); } return [ 'href' => $xpath->evaluate('string(d:href)'), 'calendarUri' => $this->server->calculateUri($hostHref), 'inReplyTo' => $xpath->evaluate('string(cs:in-reply-to)'), 'summary' => $xpath->evaluate('string(cs:summary)'), 'status' => $xpath->evaluate('boolean(cs:invite-accepted)')?self::STATUS_ACCEPTED:self::STATUS_DECLINED ]; } } sabre-dav-2.1.10/lib/CalDAV/Subscriptions/000077500000000000000000000000001267035675400201255ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CalDAV/Subscriptions/ISubscription.php000066400000000000000000000023221267035675400234320ustar00rootroot00000000000000resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] = '{http://calendarserver.org/ns/}subscribed'; $server->propertyMap['{http://calendarserver.org/ns/}source'] = 'Sabre\\DAV\\Property\\Href'; $server->on('propFind', [$this, 'propFind'], 150); } /** * This method should return a list of server-features. * * This is for example 'versioning' and is added to the DAV: header * in an OPTIONS response. * * @return array */ function getFeatures() { return ['calendarserver-subscribed']; } /** * Triggered after properties have been fetched. * * @return void */ function propFind(PropFind $propFind, INode $node) { // There's a bunch of properties that must appear as a self-closing // xml-element. This event handler ensures that this will be the case. $props = [ '{http://calendarserver.org/ns/}subscribed-strip-alarms', '{http://calendarserver.org/ns/}subscribed-strip-attachments', '{http://calendarserver.org/ns/}subscribed-strip-todos', ]; foreach($props as $prop) { if ($propFind->getStatus($prop)===200) { $propFind->set($prop, '', 200); } } } } sabre-dav-2.1.10/lib/CalDAV/Subscriptions/Subscription.php000066400000000000000000000146441267035675400233330ustar00rootroot00000000000000caldavBackend = $caldavBackend; $this->subscriptionInfo = $subscriptionInfo; $required = [ 'id', 'uri', 'principaluri', 'source', ]; foreach($required as $r) { if (!isset($subscriptionInfo[$r])) { throw new \InvalidArgumentException('The ' . $r . ' field is required when creating a subscription node'); } } } /** * Returns the name of the node. * * This is used to generate the url. * * @return string */ function getName() { return $this->subscriptionInfo['uri']; } /** * Returns the last modification time * * @return int */ function getLastModified() { if (isset($this->subscriptionInfo['lastmodified'])) { return $this->subscriptionInfo['lastmodified']; } } /** * Deletes the current node * * @return void */ function delete() { $this->caldavBackend->deleteSubscription( $this->subscriptionInfo['id'] ); } /** * Returns an array with all the child nodes * * @return DAV\INode[] */ function getChildren() { return []; } /** * Updates properties on this node. * * This method received a PropPatch object, which contains all the * information about the update. * * To update specific properties, call the 'handle' method on this object. * Read the PropPatch documentation for more information. * * @param PropPatch $propPatch * @return void */ function propPatch(PropPatch $propPatch) { return $this->caldavBackend->updateSubscription( $this->subscriptionInfo['id'], $propPatch ); } /** * 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. * * Note that it's fine to liberally give properties back, instead of * conforming to the list of requested properties. * The Server class will filter out the extra. * * @param array $properties * @return void */ function getProperties($properties) { $r = []; foreach($properties as $prop) { switch($prop) { case '{http://calendarserver.org/ns/}source' : $r[$prop] = new Href($this->subscriptionInfo['source'], false); break; default : if (isset($this->subscriptionInfo[$prop])) { $r[$prop] = $this->subscriptionInfo[$prop]; } break; } } return $r; } /** * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ function getOwner() { return $this->subscriptionInfo['principaluri']; } /** * Returns a group principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ 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 */ function getACL() { return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner() . '/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner() . '/calendar-proxy-read', 'protected' => true, ] ]; } /** * Updates the ACL. * * This method will receive a list of new ACE's. * * @param array $acl * @return void */ function setACL(array $acl) { throw new 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 */ function getSupportedPrivilegeSet() { return null; } } sabre-dav-2.1.10/lib/CalDAV/UserCalendars.php000066400000000000000000000006461267035675400205300ustar00rootroot00000000000000carddavBackend = $carddavBackend; $this->addressBookInfo = $addressBookInfo; } /** * Returns the name of the addressbook * * @return string */ function getName() { return $this->addressBookInfo['uri']; } /** * Returns a card * * @param string $name * @return \ICard */ function getChild($name) { $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name); if (!$obj) throw new DAV\Exception\NotFound('Card not found'); return new Card($this->carddavBackend,$this->addressBookInfo,$obj); } /** * Returns the full list of cards * * @return array */ function getChildren() { $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']); $children = []; foreach($objs as $obj) { $obj['acl'] = $this->getChildACL(); $children[] = new Card($this->carddavBackend,$this->addressBookInfo,$obj); } return $children; } /** * This method receives a list of paths in it's first argument. * It must return an array with Node objects. * * If any children are not found, you do not have to return them. * * @return array */ function getMultipleChildren(array $paths) { $objs = $this->carddavBackend->getMultipleCards($this->addressBookInfo['id'], $paths); $children = []; foreach($objs as $obj) { $obj['acl'] = $this->getChildACL(); $children[] = new 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 */ function createDirectory($name) { throw new 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 */ function createFile($name,$vcardData = null) { if (is_resource($vcardData)) { $vcardData = stream_get_contents($vcardData); } // Converting to UTF-8, if needed $vcardData = DAV\StringUtil::ensureUTF8($vcardData); return $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData); } /** * Deletes the entire addressbook. * * @return void */ function delete() { $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']); } /** * Renames the addressbook * * @param string $newName * @return void */ function setName($newName) { throw new DAV\Exception\MethodNotAllowed('Renaming addressbooks is not yet supported'); } /** * Returns the last modification date as a unix timestamp. * * @return void */ function getLastModified() { return null; } /** * Updates properties on this node. * * This method received a PropPatch object, which contains all the * information about the update. * * To update specific properties, call the 'handle' method on this object. * Read the PropPatch documentation for more information. * * @param DAV\PropPatch $propPatch * @return void */ function propPatch(DAV\PropPatch $propPatch) { return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $propPatch); } /** * 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 */ function getProperties($properties) { $response = []; 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 */ 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 */ 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 */ function getACL() { return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner(), 'protected' => true, ], ]; } /** * This method returns the ACL's for card nodes in this address book. * The result of this method automatically gets passed to the * card nodes in this address book. * * @return array */ function getChildACL() { return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->getOwner(), 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->getOwner(), 'protected' => true, ], ]; } /** * Updates the ACL * * This method will receive a list of new ACE's. * * @param array $acl * @return void */ function setACL(array $acl) { throw new 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 */ function getSupportedPrivilegeSet() { return null; } /** * This method returns the current sync-token for this collection. * This can be any string. * * If null is returned from this function, the plugin assumes there's no * sync information available. * * @return string|null */ function getSyncToken() { if ( $this->carddavBackend instanceof Backend\SyncSupport && isset($this->addressBookInfo['{DAV:}sync-token']) ) { return $this->addressBookInfo['{DAV:}sync-token']; } if ( $this->carddavBackend instanceof Backend\SyncSupport && isset($this->addressBookInfo['{http://sabredav.org/ns}sync-token']) ) { return $this->addressBookInfo['{http://sabredav.org/ns}sync-token']; } } /** * The getChanges method returns all the changes that have happened, since * the specified syncToken and the current collection. * * This function should return an array, such as the following: * * [ * 'syncToken' => 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'modified.txt', * ], * 'deleted' => [ * 'foo.php.bak', * 'old.txt' * ] * ]; * * The syncToken property should reflect the *current* syncToken of the * collection, as reported getSyncToken(). This is needed here too, to * ensure the operation is atomic. * * If the syncToken is specified as null, this is an initial sync, and all * members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The second argument is basically the 'depth' of the report. If it's 1, * you only have to report changes that happened only directly in immediate * descendants. If it's 2, it should also include changes from the nodes * below the child collections. (grandchildren) * * The third (optional) argument allows a client to specify how many * results should be returned at most. If the limit is not specified, it * should be treated as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChanges($syncToken, $syncLevel, $limit = null) { if (!$this->carddavBackend instanceof Backend\SyncSupport) { return null; } return $this->carddavBackend->getChangesForAddressBook( $this->addressBookInfo['id'], $syncToken, $syncLevel, $limit ); } } sabre-dav-2.1.10/lib/CardDAV/AddressBookQueryParser.php000066400000000000000000000135571267035675400225570ustar00rootroot00000000000000dom = $dom; $this->xpath = new \DOMXPath($dom); $this->xpath->registerNameSpace('card',Plugin::NS_CARDDAV); } /** * Parses the request. * * @return void */ function parse() { $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 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 DAV\Exception\BadRequest('The test attribute must either hold "anyof" or "allof"'); } $propFilters = []; if (!is_null($filter)) { $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(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 = []; $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'] = []; for($ii=0;$ii<$paramFilterNodes->length;$ii++) { $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii)); } $propFilter['text-matches'] = []; $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 */ function parseParamFilterNode(\DOMElement $paramFilterNode) { $paramFilter = []; $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 */ function parseTextMatchNode(\DOMElement $textMatchNode) { $matchType = $textMatchNode->getAttribute('match-type'); if (!$matchType) $matchType = 'contains'; if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) { throw new 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 [ 'negate-condition' => $negateCondition, 'collation' => $collation, 'match-type' => $matchType, 'value' => $textMatchNode->nodeValue ]; } } sabre-dav-2.1.10/lib/CardDAV/AddressBookRoot.php000066400000000000000000000041161267035675400212070ustar00rootroot00000000000000carddavBackend = $carddavBackend; parent::__construct($principalBackend, $principalPrefix); } /** * Returns the name of the node * * @return string */ function getName() { return 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 */ function getChildForPrincipal(array $principal) { return new UserAddressBooks($this->carddavBackend, $principal['uri']); } } sabre-dav-2.1.10/lib/CardDAV/Backend/000077500000000000000000000000001267035675400167575ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CardDAV/Backend/AbstractBackend.php000066400000000000000000000017331267035675400225070ustar00rootroot00000000000000getCard($addressBookId, $uri); }, $uris); } } sabre-dav-2.1.10/lib/CardDAV/Backend/BackendInterface.php000066400000000000000000000142211267035675400226400ustar00rootroot00000000000000pdo = $pdo; $this->addressBooksTableName = $addressBooksTableName; $this->cardsTableName = $cardsTableName; $this->addressBookChangesTableName = $addressBookChangesTableName; } /** * Returns the list of addressbooks for a specific user. * * @param string $principalUri * @return array */ function getAddressBooksForUser($principalUri) { $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, synctoken FROM '.$this->addressBooksTableName.' WHERE principaluri = ?'); $stmt->execute([$principalUri]); $addressBooks = []; foreach($stmt->fetchAll() as $row) { $addressBooks[] = [ 'id' => $row['id'], 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], '{DAV:}displayname' => $row['displayname'], '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', ]; } return $addressBooks; } /** * Updates properties for an address book. * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documenation for more info and examples. * * @param string $addressBookId * @param \Sabre\DAV\PropPatch $propPatch * @return void */ function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) { $supportedProperties = [ '{DAV:}displayname', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description', ]; $propPatch->handle($supportedProperties, function($mutations) use ($addressBookId) { $updates = []; foreach($mutations as $property=>$newValue) { switch($property) { case '{DAV:}displayname' : $updates['displayname'] = $newValue; break; case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : $updates['description'] = $newValue; break; } } $query = 'UPDATE ' . $this->addressBooksTableName . ' SET '; $first = true; foreach($updates as $key=>$value) { if ($first) { $first = false; } else { $query.=', '; } $query.=' `' . $key . '` = :' . $key . ' '; } $query.=' WHERE id = :addressbookid'; $stmt = $this->pdo->prepare($query); $updates['addressbookid'] = $addressBookId; $stmt->execute($updates); $this->addChange($addressBookId, "", 2); return true; }); } /** * Creates a new address book * * @param string $principalUri * @param string $url Just the 'basename' of the url. * @param array $properties * @return void */ function createAddressBook($principalUri, $url, array $properties) { $values = [ 'displayname' => null, 'description' => null, 'principaluri' => $principalUri, 'uri' => $url, ]; foreach($properties as $property=>$newValue) { switch($property) { case '{DAV:}displayname' : $values['displayname'] = $newValue; break; case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : $values['description'] = $newValue; break; default : throw new DAV\Exception\BadRequest('Unknown property: ' . $property); } } $query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, synctoken) VALUES (:uri, :displayname, :description, :principaluri, 1)'; $stmt = $this->pdo->prepare($query); $stmt->execute($values); return $this->pdo->lastInsertId(); } /** * Deletes an entire addressbook and all its contents * * @param int $addressBookId * @return void */ function deleteAddressBook($addressBookId) { $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); $stmt->execute([$addressBookId]); $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); $stmt->execute([$addressBookId]); $stmt = $this->pdo->prepare('DELETE FROM '.$this->addressBookChangesTableName.' WHERE addressbookid = ?'); $stmt->execute([$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 */ function getCards($addressbookId) { $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = ?'); $stmt->execute([$addressbookId]); $result = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $row['etag'] = '"' . $row['etag'] . '"'; $result[] = $row; } return $result; } /** * Returns a specfic card. * * The same set of properties must be returned as with getCards. The only * exception is that 'carddata' is absolutely required. * * If the card does not exist, you must return false. * * @param mixed $addressBookId * @param string $cardUri * @return array */ function getCard($addressBookId, $cardUri) { $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ? LIMIT 1'); $stmt->execute([$addressBookId, $cardUri]); $result = $stmt->fetch(\PDO::FETCH_ASSOC); if (!$result) return false; $result['etag'] = '"' . $result['etag'] . '"'; return $result; } /** * Returns a list of cards. * * This method should work identical to getCard, but instead return all the * cards in the list as an array. * * If the backend supports this, it may allow for some speed-ups. * * @param mixed $addressBookId * @param array $uris * @return array */ function getMultipleCards($addressBookId, array $uris) { return array_map(function($uri) use ($addressBookId) { return $this->getCard($addressBookId, $uri); }, $uris); $query = 'SELECT id, uri, lastmodified, etag, size FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = IN ('; // Inserting a whole bunch of question marks $query.=implode(',', array_fill(0, count($uris), '?')); $query.=')'; $stmt = $this->pdo->prepare($query); $stmt->execute(array_merge([$addressBookId], $uris)); $result = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $row['etag'] = '"' . $row['etag'] . '"'; $result[] = $row; } return $result; } /** * 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 */ function createCard($addressBookId, $cardUri, $cardData) { $stmt = $this->pdo->prepare('INSERT INTO ' . $this->cardsTableName . ' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (?, ?, ?, ?, ?, ?)'); $etag = md5($cardData); $stmt->execute([ $cardData, $cardUri, time(), $addressBookId, strlen($cardData), $etag, ]); $this->addChange($addressBookId, $cardUri, 1); return '"' . $etag . '"'; } /** * 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 */ function updateCard($addressBookId, $cardUri, $cardData) { $stmt = $this->pdo->prepare('UPDATE ' . $this->cardsTableName . ' SET carddata = ?, lastmodified = ?, size = ?, etag = ? WHERE uri = ? AND addressbookid =?'); $etag = md5($cardData); $stmt->execute([ $cardData, time(), strlen($cardData), $etag, $cardUri, $addressBookId ]); $this->addChange($addressBookId, $cardUri, 2); return '"' . $etag . '"'; } /** * Deletes a card * * @param mixed $addressBookId * @param string $cardUri * @return bool */ function deleteCard($addressBookId, $cardUri) { $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); $stmt->execute([$addressBookId, $cardUri]); $this->addChange($addressBookId, $cardUri, 3); return $stmt->rowCount()===1; } /** * The getChanges method returns all the changes that have happened, since * the specified syncToken in the specified address book. * * This function should return an array, such as the following: * * [ * 'syncToken' => 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'updated.txt', * ], * 'deleted' => [ * 'foo.php.bak', * 'old.txt' * ] * ]; * * The returned syncToken property should reflect the *current* syncToken * of the addressbook, as reported in the {http://sabredav.org/ns}sync-token * property. This is needed here too, to ensure the operation is atomic. * * If the $syncToken argument is specified as null, this is an initial * sync, and all members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The $syncLevel argument is basically the 'depth' of the report. If it's * 1, you only have to report changes that happened only directly in * immediate descendants. If it's 2, it should also include changes from * the nodes below the child collections. (grandchildren) * * The $limit argument allows a client to specify how many results should * be returned at most. If the limit is not specified, it should be treated * as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $addressBookId * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) { // Current synctoken $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); $stmt->execute([ $addressBookId ]); $currentToken = $stmt->fetchColumn(0); if (is_null($currentToken)) return null; $result = [ 'syncToken' => $currentToken, 'added' => [], 'modified' => [], 'deleted' => [], ]; if ($syncToken) { $query = "SELECT uri, operation FROM " . $this->addressBookChangesTableName . " WHERE synctoken >= ? AND synctoken < ? AND addressbookid = ? ORDER BY synctoken"; if ($limit>0) $query.= " LIMIT " . (int)$limit; // Fetching all changes $stmt = $this->pdo->prepare($query); $stmt->execute([$syncToken, $currentToken, $addressBookId]); $changes = []; // This loop ensures that any duplicates are overwritten, only the // last change on a node is relevant. while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $changes[$row['uri']] = $row['operation']; } foreach($changes as $uri => $operation) { switch($operation) { case 1: $result['added'][] = $uri; break; case 2: $result['modified'][] = $uri; break; case 3: $result['deleted'][] = $uri; break; } } } else { // No synctoken supplied, this is the initial sync. $query = "SELECT uri FROM " . $this->cardsTableName . " WHERE addressbookid = ?"; $stmt = $this->pdo->prepare($query); $stmt->execute([$addressBookId]); $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN); } return $result; } /** * Adds a change record to the addressbookchanges table. * * @param mixed $addressBookId * @param string $objectUri * @param int $operation 1 = add, 2 = modify, 3 = delete * @return void */ protected function addChange($addressBookId, $objectUri, $operation) { $stmt = $this->pdo->prepare('INSERT INTO ' . $this->addressBookChangesTableName .' (uri, synctoken, addressbookid, operation) SELECT ?, synctoken, ?, ? FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); $stmt->execute([ $objectUri, $addressBookId, $operation, $addressBookId ]); $stmt = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET synctoken = synctoken + 1 WHERE id = ?'); $stmt->execute([ $addressBookId ]); } } sabre-dav-2.1.10/lib/CardDAV/Backend/SyncSupport.php000066400000000000000000000053531267035675400220070ustar00rootroot00000000000000 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'modified.txt', * ], * 'deleted' => [ * 'foo.php.bak', * 'old.txt' * ] * ]; * * The returned syncToken property should reflect the *current* syncToken * of the calendar, as reported in the {http://sabredav.org/ns}sync-token * property. This is needed here too, to ensure the operation is atomic. * * If the $syncToken argument is specified as null, this is an initial * sync, and all members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The $syncLevel argument is basically the 'depth' of the report. If it's * 1, you only have to report changes that happened only directly in * immediate descendants. If it's 2, it should also include changes from * the nodes below the child collections. (grandchildren) * * The $limit argument allows a client to specify how many results should * be returned at most. If the limit is not specified, it should be treated * as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $addressBookId * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null); } sabre-dav-2.1.10/lib/CardDAV/Card.php000066400000000000000000000136011267035675400170130ustar00rootroot00000000000000carddavBackend = $carddavBackend; $this->addressBookInfo = $addressBookInfo; $this->cardData = $cardData; } /** * Returns the uri for this object * * @return string */ function getName() { return $this->cardData['uri']; } /** * Returns the VCard-formatted object * * @return string */ 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 */ function put($cardData) { if (is_resource($cardData)) $cardData = stream_get_contents($cardData); // Converting to UTF-8, if needed $cardData = 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 */ function delete() { $this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']); } /** * Returns the mime content-type * * @return string */ function getContentType() { return 'text/vcard; charset=utf-8'; } /** * Returns an ETag for this object * * @return string */ function getETag() { if (isset($this->cardData['etag'])) { return $this->cardData['etag']; } else { $data = $this->get(); if (is_string($data)) { return '"' . md5($data) . '"'; } else { // We refuse to calculate the md5 if it's a stream. return null; } } } /** * Returns the last modification date as a unix timestamp * * @return int */ function getLastModified() { return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null; } /** * Returns the size of this object in bytes * * @return int */ 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 */ 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 */ 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 */ function getACL() { // An alternative acl may be specified through the cardData array. if (isset($this->cardData['acl'])) { return $this->cardData['acl']; } return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->addressBookInfo['principaluri'], 'protected' => true, ], [ '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 */ function setACL(array $acl) { throw new 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 */ function getSupportedPrivilegeSet() { return null; } } sabre-dav-2.1.10/lib/CardDAV/IAddressBook.php000066400000000000000000000005761267035675400204620ustar00rootroot00000000000000on('propFind', [$this, 'propFindEarly']); $server->on('propFind', [$this, 'propFindLate'],150); $server->on('propPatch', [$this, 'propPatch']); $server->on('report', [$this, 'report']); $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); $server->on('onBrowserPostAction', [$this, 'browserPostAction']); $server->on('beforeWriteContent', [$this, 'beforeWriteContent']); $server->on('beforeCreateFile', [$this, 'beforeCreateFile']); $server->on('afterMethod:GET', [$this, 'httpAfterGet']); /* Namespaces */ $server->xmlNamespaces[self::NS_CARDDAV] = 'card'; /* Mapping Interfaces to {DAV:}resourcetype values */ $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; /* Adding properties that may never be changed */ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data'; $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size'; $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}addressbook-home-set'; $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-collation-set'; $server->propertyMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Property\\Href'; $this->server = $server; } /** * Returns a list of supported features. * * This is used in the DAV: header in the OPTIONS and PROPFIND requests. * * @return array */ function getFeatures() { return ['addressbook']; } /** * 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 */ function getSupportedReportSet($uri) { $node = $this->server->tree->getNodeForPath($uri); if ($node instanceof IAddressBook || $node instanceof ICard) { return [ '{' . self::NS_CARDDAV . '}addressbook-multiget', '{' . self::NS_CARDDAV . '}addressbook-query', ]; } return []; } /** * Adds all CardDAV-specific properties * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) { $ns = '{' . self::NS_CARDDAV . '}'; if ($node instanceof IAddressBook) { $propFind->handle($ns . 'max-resource-size', $this->maxResourceSize); $propFind->handle($ns . 'supported-address-data', function() { return new Property\SupportedAddressData(); }); $propFind->handle($ns . 'supported-collation-set', function() { return new Property\SupportedCollationSet(); }); } if ($node instanceof DAVACL\IPrincipal) { $path = $propFind->getPath(); $propFind->handle('{' . self::NS_CARDDAV . '}addressbook-home-set', function() use ($path) { return new DAV\Property\Href($this->getAddressBookHomeForPrincipal($path) . '/'); }); if ($this->directories) $propFind->handle('{' . self::NS_CARDDAV . '}directory-gateway', function() { return new DAV\Property\HrefList($this->directories); }); } if ($node instanceof ICard) { // The address-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. $propFind->handle('{' . self::NS_CARDDAV . '}address-data', function() use ($node) { $val = $node->get(); if (is_resource($val)) $val = stream_get_contents($val); return $val; }); } if ($node instanceof UserAddressBooks) { $propFind->handle('{http://calendarserver.org/ns/}me-card', function() use ($node) { $props = $this->server->getProperties($node->getOwner(), ['{http://sabredav.org/ns}vcard-url']); if (isset($props['{http://sabredav.org/ns}vcard-url'])) { return new DAV\Property\Href( $props['{http://sabredav.org/ns}vcard-url'] ); } }); } } /** * This event is triggered when a PROPPATCH method is executed * * @param string $path * @param DAV\PropPatch $propPatch * @return bool */ function propPatch($path, DAV\PropPatch $propPatch) { $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof UserAddressBooks) { return true; } $meCard = '{http://calendarserver.org/ns/}me-card'; $propPatch->handle($meCard, function($value) use ($node) { if ($value instanceof DAV\Property\IHref) { $value = $value->getHref(); $value = $this->server->calculateUri($value); } elseif (!is_null($value)) { return 400; } $innerResult = $this->server->updateProperties( $node->getOwner(), [ '{http://sabredav.org/ns}vcard-url' => $value, ] ); return $innerResult['{http://sabredav.org/ns}vcard-url']; }); } /** * This functions handles REPORT requests specific to CardDAV * * @param string $reportName * @param \DOMNode $dom * @return bool */ function report($reportName,$dom) { switch($reportName) { case '{'.self::NS_CARDDAV.'}addressbook-multiget' : $this->server->transactionType = 'report-addressbook-multiget'; $this->addressbookMultiGetReport($dom); return false; case '{'.self::NS_CARDDAV.'}addressbook-query' : $this->server->transactionType = 'report-addressbook-query'; $this->addressBookQueryReport($dom); return false; default : return; } } /** * Returns the addressbook home for a given principal * * @param string $principal * @return string */ protected function getAddressbookHomeForPrincipal($principal) { list(, $principalId) = \Sabre\HTTP\URLUtil::splitPath($principal); return self::ADDRESSBOOK_ROOT . '/' . $principalId; } /** * This function handles the addressbook-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 */ function addressbookMultiGetReport($dom) { $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)); $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); $propertyList = []; $uris = []; foreach($hrefElems as $elem) { $uris[] = $this->server->calculateUri($elem->nodeValue); } $xpath = new \DOMXPath($dom); $xpath->registerNameSpace('card',Plugin::NS_CARDDAV); $xpath->registerNameSpace('dav','urn:DAV'); $contentType = $xpath->evaluate("string(/card:addressbook-multiget/dav:prop/card:address-data/@content-type)"); $version = $xpath->evaluate("string(/card:addressbook-multiget/dav:prop/card:address-data/@version)"); if ($version) { $contentType.='; version=' . $version; } $vcardType = $this->negotiateVCard( $contentType ); $propertyList = []; foreach($this->server->getPropertiesForMultiplePaths($uris, $properties) as $props) { if (isset($props['200']['{' . self::NS_CARDDAV . '}address-data'])) { $props['200']['{' . self::NS_CARDDAV . '}address-data'] = $this->convertVCard( $props[200]['{' . self::NS_CARDDAV . '}address-data'], $vcardType ); } $propertyList[] = $props; } $prefer = $this->server->getHTTPPRefer(); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal'])); } /** * This method is triggered before a file gets updated with new content. * * This plugin uses this method to ensure that Card nodes receive valid * vcard data. * * @param string $path * @param DAV\IFile $node * @param resource $data * @param bool $modified Should be set to true, if this event handler * changed &$data. * @return void */ function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) { if (!$node instanceof ICard) return; $this->validateVCard($data, $modified); } /** * This method is triggered before a new file is created. * * This plugin uses this method to ensure that Card nodes receive valid * vcard data. * * @param string $path * @param resource $data * @param DAV\ICollection $parentNode * @param bool $modified Should be set to true, if this event handler * changed &$data. * @return void */ function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) { if (!$parentNode instanceof IAddressBook) return; $this->validateVCard($data, $modified); } /** * Checks if the submitted iCalendar data is in fact, valid. * * An exception is thrown if it's not. * * @param resource|string $data * @param bool $modified Should be set to true, if this event handler * changed &$data. * @return void */ protected function validateVCard(&$data, &$modified) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } $before = md5($data); // Converting the data to unicode, if needed. $data = DAV\StringUtil::ensureUTF8($data); if (md5($data) !== $before) $modified = true; try { // If the data starts with a [, we can reasonably assume we're dealing // with a jCal object. if (substr($data,0,1)==='[') { $vobj = VObject\Reader::readJson($data); // Converting $data back to iCalendar, as that's what we // technically support everywhere. $data = $vobj->serialize(); $modified = true; } else { $vobj = VObject\Reader::read($data); } } catch (VObject\ParseException $e) { throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid vCard or jCard data. Parse error: ' . $e->getMessage()); } if ($vobj->name !== 'VCARD') { throw new DAV\Exception\UnsupportedMediaType('This collection can only support vcard objects.'); } if (!isset($vobj->UID)) { // No UID in vcards is invalid, but we'll just add it in anyway. $vobj->add('UID', DAV\UUIDUtil::getUUID()); $data = $vobj->serialize(); $modified = true; } } /** * This function handles the addressbook-query REPORT * * This report is used by the client to filter an addressbook based on a * complex query. * * @param \DOMNode $dom * @return void */ protected function addressbookQueryReport($dom) { $query = new AddressBookQueryParser($dom); $query->parse(); $depth = $this->server->getHTTPDepth(0); if ($depth==0) { $candidateNodes = [ $this->server->tree->getNodeForPath($this->server->getRequestUri()) ]; if (!$candidateNodes[0] instanceof ICard) { throw new ReportNotSupported('The addressbook-query report is not supported on this url with Depth: 0'); } } else { $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri()); } $xpath = new \DOMXPath($dom); $xpath->registerNameSpace('card',Plugin::NS_CARDDAV); $xpath->registerNameSpace('dav','urn:DAV'); $contentType = $xpath->evaluate("string(/card:addressbook-query/dav:prop/card:address-data/@content-type)"); $version = $xpath->evaluate("string(/card:addressbook-query/dav:prop/card:address-data/@version)"); if ($version) { $contentType.='; version=' . $version; } $vcardType = $this->negotiateVCard( $contentType ); $validNodes = []; foreach($candidateNodes as $node) { if (!$node instanceof ICard) continue; $blob = $node->get(); if (is_resource($blob)) { $blob = stream_get_contents($blob); } if (!$this->validateFilters($blob, $query->filters, $query->test)) { continue; } $validNodes[] = $node; if ($query->limit && $query->limit <= count($validNodes)) { // We hit the maximum number of items, we can stop now. break; } } $result = []; foreach($validNodes as $validNode) { if ($depth==0) { $href = $this->server->getRequestUri(); } else { $href = $this->server->getRequestUri() . '/' . $validNode->getName(); } list($props) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0); if (isset($props[200]['{' . self::NS_CARDDAV . '}address-data'])) { $props[200]['{' . self::NS_CARDDAV . '}address-data'] = $this->convertVCard( $props[200]['{' . self::NS_CARDDAV . '}address-data'], $vcardType ); } $result[] = $props; } $prefer = $this->server->getHTTPPRefer(); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); } /** * Validates if a vcard makes it throught a list of filters. * * @param string $vcardData * @param array $filters * @param string $test anyof or allof (which means OR or AND) * @return bool */ function validateFilters($vcardData, array $filters, $test) { $vcard = VObject\Reader::read($vcardData); if (!$filters) return true; foreach($filters as $filter) { $isDefined = isset($vcard->{$filter['name']}); if ($filter['is-not-defined']) { if ($isDefined) { $success = false; } else { $success = true; } } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) { // We only need to check for existence $success = $isDefined; } else { $vProperties = $vcard->select($filter['name']); $results = []; if ($filter['param-filters']) { $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']); } if ($filter['text-matches']) { $texts = []; foreach($vProperties as $vProperty) $texts[] = $vProperty->getValue(); $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']); } if (count($results)===1) { $success = $results[0]; } else { if ($filter['test'] === 'anyof') { $success = $results[0] || $results[1]; } else { $success = $results[0] && $results[1]; } } } // else // There are two conditions where we can already determine whether // or not this filter succeeds. if ($test==='anyof' && $success) { return true; } if ($test==='allof' && !$success) { return false; } } // foreach // If we got all the way here, it means we haven't been able to // determine early if the test failed or not. // // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. return $test==='allof'; } /** * Validates if a param-filter can be applied to a specific property. * * @todo currently we're only validating the first parameter of the passed * property. Any subsequence parameters with the same name are * ignored. * @param array $vProperties * @param array $filters * @param string $test * @return bool */ protected function validateParamFilters(array $vProperties, array $filters, $test) { foreach($filters as $filter) { $isDefined = false; foreach($vProperties as $vProperty) { $isDefined = isset($vProperty[$filter['name']]); if ($isDefined) break; } if ($filter['is-not-defined']) { if ($isDefined) { $success = false; } else { $success = true; } // If there's no text-match, we can just check for existence } elseif (!$filter['text-match'] || !$isDefined) { $success = $isDefined; } else { $success = false; foreach($vProperties as $vProperty) { // If we got all the way here, we'll need to validate the // text-match filter. $success = DAV\StringUtil::textMatch($vProperty[$filter['name']]->getValue(), $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']); if ($success) break; } if ($filter['text-match']['negate-condition']) { $success = !$success; } } // else // There are two conditions where we can already determine whether // or not this filter succeeds. if ($test==='anyof' && $success) { return true; } if ($test==='allof' && !$success) { return false; } } // If we got all the way here, it means we haven't been able to // determine early if the test failed or not. // // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. return $test==='allof'; } /** * Validates if a text-filter can be applied to a specific property. * * @param array $texts * @param array $filters * @param string $test * @return bool */ protected function validateTextMatches(array $texts, array $filters, $test) { foreach($filters as $filter) { $success = false; foreach($texts as $haystack) { $success = DAV\StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']); // Breaking on the first match if ($success) break; } if ($filter['negate-condition']) { $success = !$success; } if ($success && $test==='anyof') return true; if (!$success && $test=='allof') return false; } // If we got all the way here, it means we haven't been able to // determine early if the test failed or not. // // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. return $test==='allof'; } /** * This event is triggered when fetching properties. * * This event is scheduled late in the process, after most work for * propfind has been done. */ function propFindLate(DAV\PropFind $propFind, DAV\INode $node) { // If the request was made using the SOGO connector, we must rewrite // the content-type property. By default SabreDAV will send back // text/x-vcard; charset=utf-8, but for SOGO we must strip that last // part. if (strpos($this->server->httpRequest->getHeader('User-Agent'),'Thunderbird')===false) { return; } $contentType = $propFind->get('{DAV:}getcontenttype'); list($part) = explode(';', $contentType); if ($part === 'text/x-vcard' || $part === 'text/vcard') { $propFind->set('{DAV:}getcontenttype', 'text/x-vcard'); } } /** * 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 addressbooks. * * @param DAV\INode $node * @param string $output * @return bool */ function htmlActionsPanel(DAV\INode $node, &$output) { if (!$node instanceof UserAddressBooks) return; $output.= '

Create new address book



'; return false; } /** * This event is triggered after GET requests. * * This is used to transform data into jCal, if this was requested. * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function httpAfterGet(RequestInterface $request, ResponseInterface $response) { if (strpos($response->getHeader('Content-Type'),'text/vcard')===false) { return; } $target = $this->negotiateVCard($request->getHeader('Accept'), $mimeType); $newBody = $this->convertVCard( $response->getBody(), $target ); $response->setBody($newBody); $response->setHeader('Content-Type', $mimeType . '; charset=utf-8'); $response->setHeader('Content-Length', strlen($newBody)); } /** * This method allows us to intercept the 'mkaddressbook' sabreAction. This * action enables the user to create new addressbooks from the browser plugin. * * @param string $uri * @param string $action * @param array $postVars * @return bool */ function browserPostAction($uri, $action, array $postVars) { if ($action!=='mkaddressbook') return; $resourceType = ['{DAV:}collection','{urn:ietf:params:xml:ns:carddav}addressbook']; $properties = []; if (isset($postVars['{DAV:}displayname'])) { $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname']; } $this->server->createCollection($uri . '/' . $postVars['name'],$resourceType,$properties); return false; } /** * This helper function performs the content-type negotiation for vcards. * * It will return one of the following strings: * 1. vcard3 * 2. vcard4 * 3. jcard * * It defaults to vcard3. * * @param string $input * @param string $mimeType * @return string */ protected function negotiateVCard($input, &$mimeType = null) { $result = HTTP\Util::negotiate( $input, [ // Most often used mime-type. Version 3 'text/x-vcard', // The correct standard mime-type. Defaults to version 3 as // well. 'text/vcard', // vCard 4 'text/vcard; version=4.0', // vCard 3 'text/vcard; version=3.0', // jCard 'application/vcard+json', ] ); $mimeType = $result; switch($result) { default : case 'text/x-vcard' : case 'text/vcard' : case 'text/vcard; version=3.0' : $mimeType = 'text/vcard'; return 'vcard3'; case 'text/vcard; version=4.0' : return 'vcard4'; case 'application/vcard+json' : return 'jcard'; // @codeCoverageIgnoreStart } // @codeCoverageIgnoreEnd } /** * Converts a vcard blob to a different version, or jcard. * * @param string $data * @param string $target * @return string */ protected function convertVCard($data, $target) { $data = VObject\Reader::read($data); switch($target) { default : case 'vcard3' : $data = $data->convert(VObject\Document::VCARD30); return $data->serialize(); case 'vcard4' : $data = $data->convert(VObject\Document::VCARD40); return $data->serialize(); case 'jcard' : $data = $data->convert(VObject\Document::VCARD40); return json_encode($data->jsonSerialize()); // @codeCoverageIgnoreStart } // @codeCoverageIgnoreEnd } } sabre-dav-2.1.10/lib/CardDAV/Property/000077500000000000000000000000001267035675400172545ustar00rootroot00000000000000sabre-dav-2.1.10/lib/CardDAV/Property/SupportedAddressData.php000066400000000000000000000035461267035675400240620ustar00rootroot00000000000000 'text/vcard', 'version' => '3.0'], ['contentType' => 'text/vcard', 'version' => '4.0'], ['contentType' => 'application/vcard+json', 'version' => '4.0'], ]; } $this->supportedData = $supportedData; } /** * Serializes the property in a DOMDocument * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server,\DOMElement $node) { $doc = $node->ownerDocument; $prefix = isset($server->xmlNamespaces[CardDAV\Plugin::NS_CARDDAV]) ? $server->xmlNamespaces[CardDAV\Plugin::NS_CARDDAV] : 'card'; foreach($this->supportedData as $supported) { $caldata = $doc->createElementNS(CardDAV\Plugin::NS_CARDDAV, $prefix . ':address-data-type'); $caldata->setAttribute('content-type',$supported['contentType']); $caldata->setAttribute('version',$supported['version']); $node->appendChild($caldata); } } } sabre-dav-2.1.10/lib/CardDAV/Property/SupportedCollationSet.php000066400000000000000000000022261267035675400242750ustar00rootroot00000000000000ownerDocument; $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:carddav'); if (!$prefix) $prefix = 'card'; $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') ); } } sabre-dav-2.1.10/lib/CardDAV/UserAddressBooks.php000066400000000000000000000133371267035675400213720ustar00rootroot00000000000000carddavBackend = $carddavBackend; $this->principalUri = $principalUri; } /** * Returns the name of this object * * @return string */ function getName() { list(,$name) = URLUtil::splitPath($this->principalUri); return $name; } /** * Updates the name of this object * * @param string $name * @return void */ function setName($name) { throw new DAV\Exception\MethodNotAllowed(); } /** * Deletes this object * * @return void */ function delete() { throw new DAV\Exception\MethodNotAllowed(); } /** * Returns the last modification date * * @return int */ function getLastModified() { return null; } /** * Creates a new file under this object. * * This is currently not allowed * * @param string $filename * @param resource $data * @return void */ function createFile($filename, $data=null) { throw new 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 */ function createDirectory($filename) { throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); } /** * Returns a single addressbook, by name * * @param string $name * @todo needs optimizing * @return \AddressBook */ function getChild($name) { foreach($this->getChildren() as $child) { if ($name==$child->getName()) return $child; } throw new DAV\Exception\NotFound('Addressbook with name \'' . $name . '\' could not be found'); } /** * Returns a list of addressbooks * * @return array */ function getChildren() { $addressbooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri); $objs = []; foreach($addressbooks as $addressbook) { $objs[] = new AddressBook($this->carddavBackend, $addressbook); } return $objs; } /** * Creates a new addressbook * * @param string $name * @param array $resourceType * @param array $properties * @return void */ function createExtendedCollection($name, array $resourceType, array $properties) { if (!in_array('{'.Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) { throw new DAV\Exception\InvalidResourceType('Unknown resourceType for this collection'); } $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties); } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ 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 */ 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 */ function getACL() { return [ [ 'privilege' => '{DAV:}read', 'principal' => $this->principalUri, 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => $this->principalUri, 'protected' => true, ], ]; } /** * Updates the ACL * * This method will receive a list of new ACE's. * * @param array $acl * @return void */ function setACL(array $acl) { throw new 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 */ function getSupportedPrivilegeSet() { return null; } } sabre-dav-2.1.10/lib/CardDAV/VCFExportPlugin.php000066400000000000000000000053601267035675400211440ustar00rootroot00000000000000server = $server; $this->server->on('method:GET', [$this,'httpGet'], 90); } /** * Intercepts GET requests on addressbook urls ending with ?export. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpGet(RequestInterface $request, ResponseInterface $response) { $queryParams = $request->getQueryParameters(); if (!array_key_exists('export', $queryParams)) return; $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path); if (!($node instanceof IAddressBook)) return; $this->server->transactionType = 'get-addressbook-export'; // Checking ACL, if available. if ($aclPlugin = $this->server->getPlugin('acl')) { $aclPlugin->checkPrivileges($path, '{DAV:}read'); } $response->setHeader('Content-Type','text/directory'); $response->setStatus(200); $nodes = $this->server->getPropertiesForPath($path, [ '{' . Plugin::NS_CARDDAV . '}address-data', ],1); $response->setBody($this->generateVCF($nodes)); // Returning false to break the event chain return false; } /** * Merges all vcard objects, and builds one big vcf export * * @param array $nodes * @return string */ function generateVCF(array $nodes) { $output = ""; foreach($nodes as $node) { if (!isset($node[200]['{' . Plugin::NS_CARDDAV . '}address-data'])) { continue; } $nodeData = $node[200]['{' . Plugin::NS_CARDDAV . '}address-data']; // Parsing this node so VObject can clean up the output. $output .= VObject\Reader::read($nodeData)->serialize(); } return $output; } } sabre-dav-2.1.10/lib/DAV/000077500000000000000000000000001267035675400146365ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAV/Auth/000077500000000000000000000000001267035675400155375ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAV/Auth/Backend/000077500000000000000000000000001267035675400170665ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAV/Auth/Backend/AbstractBasic.php000066400000000000000000000044601267035675400223100ustar00rootroot00000000000000currentUser; } /** * Authenticates the user based on the current request. * * If authentication is successful, true must be returned. * If authentication fails, an exception must be thrown. * * @param DAV\Server $server * @param string $realm * @throws DAV\Exception\NotAuthenticated * @return bool */ function authenticate(DAV\Server $server, $realm) { $auth = new HTTP\Auth\Basic($realm, $server->httpRequest, $server->httpResponse); $userpass = $auth->getCredentials($server->httpRequest); if (!$userpass) { $auth->requireLogin(); throw new DAV\Exception\NotAuthenticated('No basic authentication headers were found'); } // Authenticates the user if (!$this->validateUserPass($userpass[0],$userpass[1])) { $auth->requireLogin(); throw new DAV\Exception\NotAuthenticated('Username or password does not match'); } $this->currentUser = $userpass[0]; return true; } } sabre-dav-2.1.10/lib/DAV/Auth/Backend/AbstractDigest.php000066400000000000000000000052041267035675400225030ustar00rootroot00000000000000httpRequest, $server->httpResponse); $digest->init(); $username = $digest->getUsername(); // No username was given if (!$username) { $digest->requireLogin(); throw new DAV\Exception\NotAuthenticated('No digest authentication headers were found'); } $hash = $this->getDigestHash($realm, $username); // If this was false, the user account didn't exist if ($hash===false || is_null($hash)) { $digest->requireLogin(); throw new DAV\Exception\NotAuthenticated('The supplied username was not on file'); } if (!is_string($hash)) { throw new DAV\Exception('The returned value from getDigestHash must be a string or null'); } // If this was false, the password or part of the hash was incorrect. if (!$digest->validateA1($hash)) { $digest->requireLogin(); throw new DAV\Exception\NotAuthenticated('Incorrect username'); } $this->currentUser = $username; return true; } /** * Returns the currently logged in username. * * @return string|null */ function getCurrentUser() { return $this->currentUser; } } sabre-dav-2.1.10/lib/DAV/Auth/Backend/Apache.php000066400000000000000000000032261267035675400207630ustar00rootroot00000000000000httpRequest->getRawServerValue('REMOTE_USER'); if (is_null($remoteUser)) { $remoteUser = $server->httpRequest->getRawServerValue('REDIRECT_REMOTE_USER'); } if (is_null($remoteUser)) { throw new DAV\Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured'); } $this->remoteUser = $remoteUser; return true; } /** * Returns information about the currently logged in user. * * If nobody is currently logged in, this method should return null. * * @return array|null */ function getCurrentUser() { return $this->remoteUser; } } sabre-dav-2.1.10/lib/DAV/Auth/Backend/BackendInterface.php000066400000000000000000000016021267035675400227460ustar00rootroot00000000000000callBack = $callBack; } /** * Validates a username and password * * This method should return true or false depending on if login * succeeded. * * @param string $username * @param string $password * @return bool */ protected function validateUserPass($username, $password) { $cb = $this->callBack; return $cb($username, $password); } } sabre-dav-2.1.10/lib/DAV/Auth/Backend/File.php000066400000000000000000000034711267035675400204630ustar00rootroot00000000000000loadFile($filename); } /** * Loads an htdigest-formatted file. This method can be called multiple times if * more than 1 file is used. * * @param string $filename * @return void */ function loadFile($filename) { foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) { if (substr_count($line, ":") !== 2) throw new DAV\Exception('Malformed htdigest file. Every line should contain 2 colons'); list($username,$realm,$A1) = explode(':',$line); if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) throw new DAV\Exception('Malformed htdigest file. Invalid md5 hash'); $this->users[$realm . ':' . $username] = $A1; } } /** * Returns a users' information * * @param string $realm * @param string $username * @return string */ function getDigestHash($realm, $username) { return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false; } } sabre-dav-2.1.10/lib/DAV/Auth/Backend/PDO.php000066400000000000000000000026471267035675400202320ustar00rootroot00000000000000pdo = $pdo; $this->tableName = $tableName; } /** * Returns the digest hash for a user. * * @param string $realm * @param string $username * @return string|null */ function getDigestHash($realm,$username) { $stmt = $this->pdo->prepare('SELECT digesta1 FROM '.$this->tableName.' WHERE username = ?'); $stmt->execute([$username]); return $stmt->fetchColumn() ?: null; } } sabre-dav-2.1.10/lib/DAV/Auth/Plugin.php000066400000000000000000000050661267035675400175150ustar00rootroot00000000000000realm; } /** * __construct * * @param Backend\BackendInterface $authBackend * @param string $realm */ function __construct(Backend\BackendInterface $authBackend, $realm) { $this->authBackend = $authBackend; $this->realm = $realm; } /** * Initializes the plugin. This function is automatically called by the server * * @param DAV\Server $server * @return void */ function initialize(DAV\Server $server) { $this->server = $server; $this->server->on('beforeMethod', [$this,'beforeMethod'], 10); } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using DAV\Server::getPlugin * * @return string */ function getPluginName() { return 'auth'; } /** * Returns the current users' principal uri. * * If nobody is logged in, this will return null. * * @return string|null */ function getCurrentUser() { $userInfo = $this->authBackend->getCurrentUser(); if (!$userInfo) return null; return $userInfo; } /** * This method is called before any HTTP method and forces users to be authenticated * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function beforeMethod(RequestInterface $request, ResponseInterface $response) { $this->authBackend->authenticate($this->server,$this->getRealm()); } } sabre-dav-2.1.10/lib/DAV/Browser/000077500000000000000000000000001267035675400162615ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAV/Browser/GuessContentType.php000066400000000000000000000045401267035675400222600ustar00rootroot00000000000000 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', // groupware 'ics' => 'text/calendar', 'vcf' => 'text/x-vcard', // text 'txt' => 'text/plain', ]; /** * Initializes the plugin * * @param DAV\Server $server * @return void */ function initialize(DAV\Server $server) { // Using a relatively low priority (200) to allow other extensions // to set the content-type first. $server->on('propFind', [$this,'propFind'], 200); } /** * Our PROPFIND handler * * Here we set a contenttype, if the node didn't already have one. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { $propFind->handle('{DAV:}getcontenttype', function() use ($propFind) { list(, $fileName) = URLUtil::splitPath($propFind->getPath()); return $this->getContentType($fileName); }); } /** * Simple method to return the contenttype * * @param string $fileName * @return string */ protected function getContentType($fileName) { // Just grabbing the extension $extension = strtolower(substr($fileName,strrpos($fileName,'.')+1)); if (isset($this->extensionMap[$extension])) return $this->extensionMap[$extension]; } } sabre-dav-2.1.10/lib/DAV/Browser/MapGetToPropFind.php000066400000000000000000000027341267035675400221220ustar00rootroot00000000000000server = $server; $this->server->on('method:GET', [$this,'httpGet'], 90); } /** * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpGet(RequestInterface $request, ResponseInterface $response) { $node = $this->server->tree->getNodeForPath($request->getPath()); if ($node instanceof DAV\IFile) return; $subRequest = clone $request; $subRequest->setMethod('PROPFIND'); $this->server->invokeMethod($subRequest,$response); return false; } } sabre-dav-2.1.10/lib/DAV/Browser/Plugin.php000066400000000000000000000541341267035675400202370ustar00rootroot00000000000000enablePost = $enablePost; } /** * Initializes the plugin and subscribes to events * * @param DAV\Server $server * @return void */ function initialize(DAV\Server $server) { $this->server = $server; $this->server->on('method:GET', [$this,'httpGet'], 200); $this->server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel'],200); if ($this->enablePost) $this->server->on('method:POST', [$this,'httpPOST']); } /** * This method intercepts GET requests to collections and returns the html * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpGet(RequestInterface $request, ResponseInterface $response) { // We're not using straight-up $_GET, because we want everything to be // unit testable. $getVars = $request->getQueryParameters(); $sabreAction = isset($getVars['sabreAction'])?$getVars['sabreAction']:null; // Asset handling, such as images if ($sabreAction === 'asset' && isset($getVars['assetName'])) { $this->serveAsset($getVars['assetName']); return false; } try { $this->server->tree->getNodeForPath($request->getPath()); } catch (DAV\Exception\NotFound $e) { // We're simply stopping when the file isn't found to not interfere // with other plugins. return; } $response->setStatus(200); $response->setHeader('Content-Type','text/html; charset=utf-8'); $response->setBody( $this->generateDirectoryIndex($request->getPath()) ); return false; } /** * Handles POST requests for tree operations. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpPOST(RequestInterface $request, ResponseInterface $response) { $contentType = $request->getHeader('Content-Type'); list($contentType) = explode(';', $contentType); if ($contentType !== 'application/x-www-form-urlencoded' && $contentType !== 'multipart/form-data') { return; } $postVars = $request->getPostData(); if (!isset($postVars['sabreAction'])) return; $uri = $request->getPath(); if ($this->server->emit('onBrowserPostAction', [$uri, $postVars['sabreAction'], $postVars])) { switch($postVars['sabreAction']) { case 'mkcol' : if (isset($postVars['name']) && trim($postVars['name'])) { // Using basename() because we won't allow slashes list(, $folderName) = URLUtil::splitPath(trim($postVars['name'])); $this->server->createDirectory($uri . '/' . $folderName); } break; // @codeCoverageIgnoreStart case 'put' : if ($_FILES) $file = current($_FILES); else break; list(, $newName) = URLUtil::splitPath(trim($file['name'])); if (isset($postVars['name']) && trim($postVars['name'])) $newName = trim($postVars['name']); // Making sure we only have a 'basename' component list(, $newName) = URLUtil::splitPath($newName); if (is_uploaded_file($file['tmp_name'])) { $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'],'r')); } break; // @codeCoverageIgnoreEnd } } $response->setHeader('Location', $request->getUrl()); $response->setStatus(302); return false; } /** * Escapes a string for html. * * @param string $value * @return string */ function escapeHTML($value) { return htmlspecialchars($value,ENT_QUOTES,'UTF-8'); } /** * Generates the html directory index for a given url * * @param string $path * @return string */ function generateDirectoryIndex($path) { $version = ''; if (DAV\Server::$exposeVersion) { $version = DAV\Version::VERSION; } $vars = [ 'path' => $this->escapeHTML($path), 'favicon' => $this->escapeHTML($this->getAssetUrl('favicon.ico')), 'style' => $this->escapeHTML($this->getAssetUrl('sabredav.css')), 'iconstyle' => $this->escapeHTML($this->getAssetUrl('openiconic/open-iconic.css')), 'logo' => $this->escapeHTML($this->getAssetUrl('sabredav.png')), 'baseUrl' => $this->server->getBaseUri(), ]; $html = << $vars[path]/ - sabre/dav $version
"; $node = $this->server->tree->getNodeForPath($path); if ($node instanceof DAV\ICollection) { $html.="

Nodes

\n"; $html.=""; $subNodes = $this->server->getPropertiesForChildren($path, [ '{DAV:}displayname', '{DAV:}resourcetype', '{DAV:}getcontenttype', '{DAV:}getcontentlength', '{DAV:}getlastmodified', ]); foreach($subNodes as $subPath=>$subProps) { $subNode = $this->server->tree->getNodeForPath($subPath); $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($subPath); list(, $displayPath) = URLUtil::splitPath($subPath); $subNodes[$subPath]['subNode'] = $subNode; $subNodes[$subPath]['fullPath'] = $fullPath; $subNodes[$subPath]['displayPath'] = $displayPath; } uasort($subNodes, [$this, 'compareNodes']); foreach($subNodes as $subProps) { $type = [ 'string' => 'Unknown', 'icon' => 'cog', ]; if (isset($subProps['{DAV:}resourcetype'])) { $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']); } $html.= ''; $html.= ''; $html.= ''; $html.= ''; } $html.= '
' . $this->escapeHTML($subProps['displayPath']) . '' . $this->escapeHTML($type['string']) . ''; if (isset($subProps['{DAV:}getcontentlength'])) { $html.=$this->escapeHTML($subProps['{DAV:}getcontentlength'] . ' bytes'); } $html.= ''; if (isset($subProps['{DAV:}getlastmodified'])) { $lastMod = $subProps['{DAV:}getlastmodified']->getTime(); $html.=$this->escapeHTML($lastMod->format('F j, Y, g:i a')); } $html.= '
'; } $html.="
"; $html.="

Properties

"; $html.=""; // Allprops request $propFind = new PropFindAll($path); $properties = $this->server->getPropertiesByNode($propFind, $node); $properties = $propFind->getResultForMultiStatus()[200]; foreach($properties as $propName => $propValue) { $html.=$this->drawPropertyRow($propName, $propValue); } $html.="
"; $html.="
"; /* Start of generating actions */ $output = ''; if ($this->enablePost) { $this->server->emit('onHTMLActionsPanel', [$node, &$output]); } if ($output) { $html.="

Actions

"; $html.="
\n"; $html.=$output; $html.="
\n"; $html.="
\n"; } $html.= " "; $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';"); return $html; } /** * This method is used to generate the 'actions panel' output for * collections. * * This specifically generates the interfaces for creating new files, and * creating new directories. * * @param DAV\INode $node * @param mixed $output * @return void */ function htmlActionsPanel(DAV\INode $node, &$output) { if (!$node instanceof DAV\ICollection) return; // We also know fairly certain that if an object is a non-extended // SimpleCollection, we won't need to show the panel either. if (get_class($node)==='Sabre\\DAV\\SimpleCollection') return; ob_start(); echo '

Create new folder


Upload file



'; $output.=ob_get_clean(); } /** * This method takes a path/name of an asset and turns it into url * suiteable for http access. * * @param string $assetName * @return string */ protected function getAssetUrl($assetName) { return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); } /** * This method returns a local pathname to an asset. * * @param string $assetName * @return string * @throws DAV\Exception\NotFound */ protected function getLocalAssetPath($assetName) { $assetDir = __DIR__ . '/assets/'; $path = $assetDir . $assetName; // Making sure people aren't trying to escape from the base path. $path = str_replace('\\', '/', $path); if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') { throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); } if (strpos(realpath($path), realpath($assetDir)) === 0 && file_exists($path)) { return $path; } throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); } /** * This method reads an asset from disk and generates a full http response. * * @param string $assetName * @return void */ protected function serveAsset($assetName) { $assetPath = $this->getLocalAssetPath($assetName); // Rudimentary mime type detection $mime = 'application/octet-stream'; $map = [ 'ico' => 'image/vnd.microsoft.icon', 'png' => 'image/png', 'css' => 'text/css', ]; $ext = substr($assetName, strrpos($assetName, '.')+1); if (isset($map[$ext])) { $mime = $map[$ext]; } $this->server->httpResponse->setHeader('Content-Type', $mime); $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath)); $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); $this->server->httpResponse->setStatus(200); $this->server->httpResponse->setBody(fopen($assetPath,'r')); } /** * Sort helper function: compares two directory entries based on type and * display name. Collections sort above other types. * * @param array $a * @param array $b * @return int */ protected function compareNodes($a, $b) { $typeA = (isset($a['{DAV:}resourcetype'])) ? (in_array('{DAV:}collection', $a['{DAV:}resourcetype']->getValue())) : false; $typeB = (isset($b['{DAV:}resourcetype'])) ? (in_array('{DAV:}collection', $b['{DAV:}resourcetype']->getValue())) : false; // If same type, sort alphabetically by filename: if ($typeA === $typeB) { return strnatcasecmp($a['displayPath'], $b['displayPath']); } return (($typeA < $typeB) ? 1 : -1); } /** * Maps a resource type to a human-readable string and icon. * * @param array $resourceTypes * @param INode $node * @return array */ private function mapResourceType(array $resourceTypes, $node) { if (!$resourceTypes) { if ($node instanceof DAV\IFile) { return [ 'string' => 'File', 'icon' => 'file', ]; } else { return [ 'string' => 'Unknown', 'icon' => 'cog', ]; } } $types = [ '{http://calendarserver.org/ns/}calendar-proxy-write' => [ 'string' => 'Proxy-Write', 'icon' => 'people', ], '{http://calendarserver.org/ns/}calendar-proxy-read' => [ 'string' => 'Proxy-Read', 'icon' => 'people', ], '{urn:ietf:params:xml:ns:caldav}schedule-outbox' => [ 'string' => 'Outbox', 'icon' => 'inbox', ], '{urn:ietf:params:xml:ns:caldav}schedule-inbox' => [ 'string' => 'Inbox', 'icon' => 'inbox', ], '{urn:ietf:params:xml:ns:caldav}calendar' => [ 'string' => 'Calendar', 'icon' => 'calendar', ], '{http://calendarserver.org/ns/}shared-owner' => [ 'string' => 'Shared', 'icon' => 'calendar', ], '{http://calendarserver.org/ns/}subscribed' => [ 'string' => 'Subscription', 'icon' => 'calendar', ], '{urn:ietf:params:xml:ns:carddav}directory' => [ 'string' => 'Directory', 'icon' => 'globe', ], '{urn:ietf:params:xml:ns:carddav}addressbook' => [ 'string' => 'Address book', 'icon' => 'book', ], '{DAV:}principal' => [ 'string' => 'Principal', 'icon' => 'person', ], '{DAV:}collection' => [ 'string' => 'Collection', 'icon' => 'folder', ], ]; $info = [ 'string' => [], 'icon' => 'cog', ]; foreach($resourceTypes as $k=> $resourceType) { if (isset($types[$resourceType])) { $info['string'][] = $types[$resourceType]['string']; } else { $info['string'][] = $resourceType; } } foreach($types as $key=>$resourceInfo) { if (in_array($key, $resourceTypes)) { $info['icon'] = $resourceInfo['icon']; break; } } $info['string'] = implode(', ', $info['string']); return $info; } /** * Draws a table row for a property * * @param string $name * @param mixed $value * @return string */ private function drawPropertyRow($name, $value) { $view = 'unknown'; if (is_scalar($value)) { $view = 'string'; } elseif($value instanceof DAV\Property) { $mapping = [ 'Sabre\\DAV\\Property\\IHref' => 'href', 'Sabre\\DAV\\Property\\HrefList' => 'hreflist', 'Sabre\\DAV\\Property\\SupportedMethodSet' => 'valuelist', 'Sabre\\DAV\\Property\\ResourceType' => 'xmlvaluelist', 'Sabre\\DAV\\Property\\SupportedReportSet' => 'xmlvaluelist', 'Sabre\\DAVACL\\Property\\CurrentUserPrivilegeSet' => 'xmlvaluelist', 'Sabre\\DAVACL\\Property\\SupportedPrivilegeSet' => 'supported-privilege-set', ]; $view = 'complex'; foreach($mapping as $class=>$val) { if ($value instanceof $class) { $view = $val; break; } } } list($ns, $localName) = DAV\XMLUtil::parseClarkNotation($name); $realName = $name; if (isset($this->server->xmlNamespaces[$ns])) { $name = $this->server->xmlNamespaces[$ns] . ':' . $localName; } ob_start(); $xmlValueDisplay = function($propName) { $realPropName = $propName; list($ns, $localName) = DAV\XMLUtil::parseClarkNotation($propName); if (isset($this->server->xmlNamespaces[$ns])) { $propName = $this->server->xmlNamespaces[$ns] . ':' . $localName; } return "escapeHTML($realPropName) . "\">" . $this->escapeHTML($propName) . ""; }; echo "escapeHTML($realName), "\">", $this->escapeHTML($name), ""; switch($view) { case 'href' : echo "server->getBaseUri() . $value->getHref() . '">' . $this->server->getBaseUri() . $value->getHref() . ''; break; case 'hreflist' : echo implode('
', array_map(function($href) { if (stripos($href,'mailto:')===0 || stripos($href,'/')===0 || stripos($href,'http:')===0 || stripos($href,'https:') === 0) { return "escapeHTML($href) . '">' . $this->escapeHTML($href) . ''; } else { return "escapeHTML($this->server->getBaseUri() . $href) . '">' . $this->escapeHTML($this->server->getBaseUri() . $href) . ''; } }, $value->getHrefs())); break; case 'xmlvaluelist' : echo implode(', ', array_map($xmlValueDisplay, $value->getValue())); break; case 'valuelist' : echo $this->escapeHTML(implode(', ', $value->getValue())); break; case 'supported-privilege-set' : $traverse = function($priv) use (&$traverse, $xmlValueDisplay) { echo "
  • "; echo $xmlValueDisplay($priv['privilege']); if (isset($priv['abstract']) && $priv['abstract']) { echo " (abstract)"; } if (isset($priv['description'])) { echo " " . $this->escapeHTML($priv['description']); } if (isset($priv['aggregates'])) { echo "\n
      \n"; foreach($priv['aggregates'] as $subPriv) { $traverse($subPriv); } echo "
    "; } echo "
  • \n"; }; echo "
      "; $traverse($value->getValue(), ''); echo "
    \n"; break; case 'string' : echo $this->escapeHTML($value); break; case 'complex' : echo 'complex'; break; default : echo 'unknown'; break; } return ob_get_clean(); } } sabre-dav-2.1.10/lib/DAV/Browser/PropFindAll.php000066400000000000000000000062771267035675400211600ustar00rootroot00000000000000handle('{DAV:}displayname', function() { * return 'hello'; * }); * * Note that handle will only work the first time. If null is returned, the * value is ignored. * * It's also possible to not pass a callback, but immediately pass a value * * @param string $propertyName * @param mixed $valueOrCallBack * @return void */ function handle($propertyName, $valueOrCallBack) { if (is_callable($valueOrCallBack)) { $value = $valueOrCallBack(); } else { $value = $valueOrCallBack; } if (!is_null($value)) { $this->result[$propertyName] = [200, $value]; } } /** * Sets the value of the property * * If status is not supplied, the status will default to 200 for non-null * properties, and 404 for null properties. * * @param string $propertyName * @param mixed $value * @param int $status * @return void */ function set($propertyName, $value, $status = null) { if (is_null($status)) { $status = is_null($value) ? 404 : 200; } $this->result[$propertyName] = [$status, $value]; } /** * Returns the current value for a property. * * @param string $propertyName * @return mixed */ function get($propertyName) { return isset($this->result[$propertyName])?$this->result[$propertyName][1]:null; } /** * Returns the current status code for a property name. * * If the property does not appear in the list of requested properties, * null will be returned. * * @param string $propertyName * @return int|null */ function getStatus($propertyName) { return isset($this->result[$propertyName])?$this->result[$propertyName][0]:404; } /** * Returns all propertynames that have a 404 status, and thus don't have a * value yet. * * @return array */ function get404Properties() { $result = []; foreach($this->result as $propertyName=>$stuff) { if ($stuff[0]===404) { $result[] = $propertyName; } } // If there's nothing in this list, we're adding one fictional item. if (!$result) { $result[] = '{http://sabredav.org/ns}idk'; } return $result; } } sabre-dav-2.1.10/lib/DAV/Browser/assets/000077500000000000000000000000001267035675400175635ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAV/Browser/assets/favicon.ico000066400000000000000000000102761267035675400217120ustar00rootroot00000000000000  ( @   5~Q ^#tV `@.w W U)+8\`w8{wN~Vl+3/Ba(#{ #+0+ ?G#9j  +F C In6 ~pgP <x<|?sabre-dav-2.1.10/lib/DAV/Browser/assets/openiconic/000077500000000000000000000000001267035675400217115ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAV/Browser/assets/openiconic/ICON-LICENSE000066400000000000000000000020611267035675400234430ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Waybury Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.sabre-dav-2.1.10/lib/DAV/Browser/assets/openiconic/open-iconic.css000066400000000000000000000333311267035675400246310ustar00rootroot00000000000000@font-face { font-family: 'Icons'; src: url('?sabreAction=asset&assetName=openiconic/open-iconic.eot'); src: url('?sabreAction=asset&assetName=openiconic/open-iconic.eot?#iconic-sm') format('embedded-opentype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.woff') format('woff'), url('?sabreAction=asset&assetName=openiconic/open-iconic.ttf') format('truetype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.otf') format('opentype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.svg#iconic-sm') format('svg'); font-weight: normal; font-style: normal; } .oi[data-glyph].oi-text-replace { font-size: 0; line-height: 0; } .oi[data-glyph].oi-text-replace:before { width: 1em; text-align: center; } .oi[data-glyph]:before { font-family: 'Icons'; display: inline-block; speak: none; line-height: 1; vertical-align: baseline; font-weight: normal; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .oi[data-glyph]:empty:before { width: 1em; text-align: center; box-sizing: content-box; } .oi[data-glyph].oi-align-left:before { text-align: left; } .oi[data-glyph].oi-align-right:before { text-align: right; } .oi[data-glyph].oi-align-center:before { text-align: center; } .oi[data-glyph].oi-flip-horizontal:before { -webkit-transform: scale(-1, 1); -ms-transform: scale(-1, 1); transform: scale(-1, 1); } .oi[data-glyph].oi-flip-vertical:before { -webkit-transform: scale(1, -1); -ms-transform: scale(-1, 1); transform: scale(1, -1); } .oi[data-glyph].oi-flip-horizontal-vertical:before { -webkit-transform: scale(-1, -1); -ms-transform: scale(-1, 1); transform: scale(-1, -1); } .oi[data-glyph=account-login]:before { content:'\e000'; } .oi[data-glyph=account-logout]:before { content:'\e001'; } .oi[data-glyph=action-redo]:before { content:'\e002'; } .oi[data-glyph=action-undo]:before { content:'\e003'; } .oi[data-glyph=align-center]:before { content:'\e004'; } .oi[data-glyph=align-left]:before { content:'\e005'; } .oi[data-glyph=align-right]:before { content:'\e006'; } .oi[data-glyph=aperture]:before { content:'\e007'; } .oi[data-glyph=arrow-bottom]:before { content:'\e008'; } .oi[data-glyph=arrow-circle-bottom]:before { content:'\e009'; } .oi[data-glyph=arrow-circle-left]:before { content:'\e00a'; } .oi[data-glyph=arrow-circle-right]:before { content:'\e00b'; } .oi[data-glyph=arrow-circle-top]:before { content:'\e00c'; } .oi[data-glyph=arrow-left]:before { content:'\e00d'; } .oi[data-glyph=arrow-right]:before { content:'\e00e'; } .oi[data-glyph=arrow-thick-bottom]:before { content:'\e00f'; } .oi[data-glyph=arrow-thick-left]:before { content:'\e010'; } .oi[data-glyph=arrow-thick-right]:before { content:'\e011'; } .oi[data-glyph=arrow-thick-top]:before { content:'\e012'; } .oi[data-glyph=arrow-top]:before { content:'\e013'; } .oi[data-glyph=audio-spectrum]:before { content:'\e014'; } .oi[data-glyph=audio]:before { content:'\e015'; } .oi[data-glyph=badge]:before { content:'\e016'; } .oi[data-glyph=ban]:before { content:'\e017'; } .oi[data-glyph=bar-chart]:before { content:'\e018'; } .oi[data-glyph=basket]:before { content:'\e019'; } .oi[data-glyph=battery-empty]:before { content:'\e01a'; } .oi[data-glyph=battery-full]:before { content:'\e01b'; } .oi[data-glyph=beaker]:before { content:'\e01c'; } .oi[data-glyph=bell]:before { content:'\e01d'; } .oi[data-glyph=bluetooth]:before { content:'\e01e'; } .oi[data-glyph=bold]:before { content:'\e01f'; } .oi[data-glyph=bolt]:before { content:'\e020'; } .oi[data-glyph=book]:before { content:'\e021'; } .oi[data-glyph=bookmark]:before { content:'\e022'; } .oi[data-glyph=box]:before { content:'\e023'; } .oi[data-glyph=briefcase]:before { content:'\e024'; } .oi[data-glyph=british-pound]:before { content:'\e025'; } .oi[data-glyph=browser]:before { content:'\e026'; } .oi[data-glyph=brush]:before { content:'\e027'; } .oi[data-glyph=bug]:before { content:'\e028'; } .oi[data-glyph=bullhorn]:before { content:'\e029'; } .oi[data-glyph=calculator]:before { content:'\e02a'; } .oi[data-glyph=calendar]:before { content:'\e02b'; } .oi[data-glyph=camera-slr]:before { content:'\e02c'; } .oi[data-glyph=caret-bottom]:before { content:'\e02d'; } .oi[data-glyph=caret-left]:before { content:'\e02e'; } .oi[data-glyph=caret-right]:before { content:'\e02f'; } .oi[data-glyph=caret-top]:before { content:'\e030'; } .oi[data-glyph=cart]:before { content:'\e031'; } .oi[data-glyph=chat]:before { content:'\e032'; } .oi[data-glyph=check]:before { content:'\e033'; } .oi[data-glyph=chevron-bottom]:before { content:'\e034'; } .oi[data-glyph=chevron-left]:before { content:'\e035'; } .oi[data-glyph=chevron-right]:before { content:'\e036'; } .oi[data-glyph=chevron-top]:before { content:'\e037'; } .oi[data-glyph=circle-check]:before { content:'\e038'; } .oi[data-glyph=circle-x]:before { content:'\e039'; } .oi[data-glyph=clipboard]:before { content:'\e03a'; } .oi[data-glyph=clock]:before { content:'\e03b'; } .oi[data-glyph=cloud-download]:before { content:'\e03c'; } .oi[data-glyph=cloud-upload]:before { content:'\e03d'; } .oi[data-glyph=cloud]:before { content:'\e03e'; } .oi[data-glyph=cloudy]:before { content:'\e03f'; } .oi[data-glyph=code]:before { content:'\e040'; } .oi[data-glyph=cog]:before { content:'\e041'; } .oi[data-glyph=collapse-down]:before { content:'\e042'; } .oi[data-glyph=collapse-left]:before { content:'\e043'; } .oi[data-glyph=collapse-right]:before { content:'\e044'; } .oi[data-glyph=collapse-up]:before { content:'\e045'; } .oi[data-glyph=command]:before { content:'\e046'; } .oi[data-glyph=comment-square]:before { content:'\e047'; } .oi[data-glyph=compass]:before { content:'\e048'; } .oi[data-glyph=contrast]:before { content:'\e049'; } .oi[data-glyph=copywriting]:before { content:'\e04a'; } .oi[data-glyph=credit-card]:before { content:'\e04b'; } .oi[data-glyph=crop]:before { content:'\e04c'; } .oi[data-glyph=dashboard]:before { content:'\e04d'; } .oi[data-glyph=data-transfer-download]:before { content:'\e04e'; } .oi[data-glyph=data-transfer-upload]:before { content:'\e04f'; } .oi[data-glyph=delete]:before { content:'\e050'; } .oi[data-glyph=dial]:before { content:'\e051'; } .oi[data-glyph=document]:before { content:'\e052'; } .oi[data-glyph=dollar]:before { content:'\e053'; } .oi[data-glyph=double-quote-sans-left]:before { content:'\e054'; } .oi[data-glyph=double-quote-sans-right]:before { content:'\e055'; } .oi[data-glyph=double-quote-serif-left]:before { content:'\e056'; } .oi[data-glyph=double-quote-serif-right]:before { content:'\e057'; } .oi[data-glyph=droplet]:before { content:'\e058'; } .oi[data-glyph=eject]:before { content:'\e059'; } .oi[data-glyph=elevator]:before { content:'\e05a'; } .oi[data-glyph=ellipses]:before { content:'\e05b'; } .oi[data-glyph=envelope-closed]:before { content:'\e05c'; } .oi[data-glyph=envelope-open]:before { content:'\e05d'; } .oi[data-glyph=euro]:before { content:'\e05e'; } .oi[data-glyph=excerpt]:before { content:'\e05f'; } .oi[data-glyph=expand-down]:before { content:'\e060'; } .oi[data-glyph=expand-left]:before { content:'\e061'; } .oi[data-glyph=expand-right]:before { content:'\e062'; } .oi[data-glyph=expand-up]:before { content:'\e063'; } .oi[data-glyph=external-link]:before { content:'\e064'; } .oi[data-glyph=eye]:before { content:'\e065'; } .oi[data-glyph=eyedropper]:before { content:'\e066'; } .oi[data-glyph=file]:before { content:'\e067'; } .oi[data-glyph=fire]:before { content:'\e068'; } .oi[data-glyph=flag]:before { content:'\e069'; } .oi[data-glyph=flash]:before { content:'\e06a'; } .oi[data-glyph=folder]:before { content:'\e06b'; } .oi[data-glyph=fork]:before { content:'\e06c'; } .oi[data-glyph=fullscreen-enter]:before { content:'\e06d'; } .oi[data-glyph=fullscreen-exit]:before { content:'\e06e'; } .oi[data-glyph=globe]:before { content:'\e06f'; } .oi[data-glyph=graph]:before { content:'\e070'; } .oi[data-glyph=grid-four-up]:before { content:'\e071'; } .oi[data-glyph=grid-three-up]:before { content:'\e072'; } .oi[data-glyph=grid-two-up]:before { content:'\e073'; } .oi[data-glyph=hard-drive]:before { content:'\e074'; } .oi[data-glyph=header]:before { content:'\e075'; } .oi[data-glyph=headphones]:before { content:'\e076'; } .oi[data-glyph=heart]:before { content:'\e077'; } .oi[data-glyph=home]:before { content:'\e078'; } .oi[data-glyph=image]:before { content:'\e079'; } .oi[data-glyph=inbox]:before { content:'\e07a'; } .oi[data-glyph=infinity]:before { content:'\e07b'; } .oi[data-glyph=info]:before { content:'\e07c'; } .oi[data-glyph=italic]:before { content:'\e07d'; } .oi[data-glyph=justify-center]:before { content:'\e07e'; } .oi[data-glyph=justify-left]:before { content:'\e07f'; } .oi[data-glyph=justify-right]:before { content:'\e080'; } .oi[data-glyph=key]:before { content:'\e081'; } .oi[data-glyph=laptop]:before { content:'\e082'; } .oi[data-glyph=layers]:before { content:'\e083'; } .oi[data-glyph=lightbulb]:before { content:'\e084'; } .oi[data-glyph=link-broken]:before { content:'\e085'; } .oi[data-glyph=link-intact]:before { content:'\e086'; } .oi[data-glyph=list-rich]:before { content:'\e087'; } .oi[data-glyph=list]:before { content:'\e088'; } .oi[data-glyph=location]:before { content:'\e089'; } .oi[data-glyph=lock-locked]:before { content:'\e08a'; } .oi[data-glyph=lock-unlocked]:before { content:'\e08b'; } .oi[data-glyph=loop-circular]:before { content:'\e08c'; } .oi[data-glyph=loop-square]:before { content:'\e08d'; } .oi[data-glyph=loop]:before { content:'\e08e'; } .oi[data-glyph=magnifying-glass]:before { content:'\e08f'; } .oi[data-glyph=map-marker]:before { content:'\e090'; } .oi[data-glyph=map]:before { content:'\e091'; } .oi[data-glyph=media-pause]:before { content:'\e092'; } .oi[data-glyph=media-play]:before { content:'\e093'; } .oi[data-glyph=media-record]:before { content:'\e094'; } .oi[data-glyph=media-skip-backward]:before { content:'\e095'; } .oi[data-glyph=media-skip-forward]:before { content:'\e096'; } .oi[data-glyph=media-step-backward]:before { content:'\e097'; } .oi[data-glyph=media-step-forward]:before { content:'\e098'; } .oi[data-glyph=media-stop]:before { content:'\e099'; } .oi[data-glyph=medical-cross]:before { content:'\e09a'; } .oi[data-glyph=menu]:before { content:'\e09b'; } .oi[data-glyph=microphone]:before { content:'\e09c'; } .oi[data-glyph=minus]:before { content:'\e09d'; } .oi[data-glyph=monitor]:before { content:'\e09e'; } .oi[data-glyph=moon]:before { content:'\e09f'; } .oi[data-glyph=move]:before { content:'\e0a0'; } .oi[data-glyph=musical-note]:before { content:'\e0a1'; } .oi[data-glyph=paperclip]:before { content:'\e0a2'; } .oi[data-glyph=pencil]:before { content:'\e0a3'; } .oi[data-glyph=people]:before { content:'\e0a4'; } .oi[data-glyph=person]:before { content:'\e0a5'; } .oi[data-glyph=phone]:before { content:'\e0a6'; } .oi[data-glyph=pie-chart]:before { content:'\e0a7'; } .oi[data-glyph=pin]:before { content:'\e0a8'; } .oi[data-glyph=play-circle]:before { content:'\e0a9'; } .oi[data-glyph=plus]:before { content:'\e0aa'; } .oi[data-glyph=power-standby]:before { content:'\e0ab'; } .oi[data-glyph=print]:before { content:'\e0ac'; } .oi[data-glyph=project]:before { content:'\e0ad'; } .oi[data-glyph=pulse]:before { content:'\e0ae'; } .oi[data-glyph=puzzle-piece]:before { content:'\e0af'; } .oi[data-glyph=question-mark]:before { content:'\e0b0'; } .oi[data-glyph=rain]:before { content:'\e0b1'; } .oi[data-glyph=random]:before { content:'\e0b2'; } .oi[data-glyph=reload]:before { content:'\e0b3'; } .oi[data-glyph=resize-both]:before { content:'\e0b4'; } .oi[data-glyph=resize-height]:before { content:'\e0b5'; } .oi[data-glyph=resize-width]:before { content:'\e0b6'; } .oi[data-glyph=rss-alt]:before { content:'\e0b7'; } .oi[data-glyph=rss]:before { content:'\e0b8'; } .oi[data-glyph=script]:before { content:'\e0b9'; } .oi[data-glyph=share-boxed]:before { content:'\e0ba'; } .oi[data-glyph=share]:before { content:'\e0bb'; } .oi[data-glyph=shield]:before { content:'\e0bc'; } .oi[data-glyph=signal]:before { content:'\e0bd'; } .oi[data-glyph=signpost]:before { content:'\e0be'; } .oi[data-glyph=sort-ascending]:before { content:'\e0bf'; } .oi[data-glyph=sort-descending]:before { content:'\e0c0'; } .oi[data-glyph=spreadsheet]:before { content:'\e0c1'; } .oi[data-glyph=star]:before { content:'\e0c2'; } .oi[data-glyph=sun]:before { content:'\e0c3'; } .oi[data-glyph=tablet]:before { content:'\e0c4'; } .oi[data-glyph=tag]:before { content:'\e0c5'; } .oi[data-glyph=tags]:before { content:'\e0c6'; } .oi[data-glyph=target]:before { content:'\e0c7'; } .oi[data-glyph=task]:before { content:'\e0c8'; } .oi[data-glyph=terminal]:before { content:'\e0c9'; } .oi[data-glyph=text]:before { content:'\e0ca'; } .oi[data-glyph=thumb-down]:before { content:'\e0cb'; } .oi[data-glyph=thumb-up]:before { content:'\e0cc'; } .oi[data-glyph=timer]:before { content:'\e0cd'; } .oi[data-glyph=transfer]:before { content:'\e0ce'; } .oi[data-glyph=trash]:before { content:'\e0cf'; } .oi[data-glyph=underline]:before { content:'\e0d0'; } .oi[data-glyph=vertical-align-bottom]:before { content:'\e0d1'; } .oi[data-glyph=vertical-align-center]:before { content:'\e0d2'; } .oi[data-glyph=vertical-align-top]:before { content:'\e0d3'; } .oi[data-glyph=video]:before { content:'\e0d4'; } .oi[data-glyph=volume-high]:before { content:'\e0d5'; } .oi[data-glyph=volume-low]:before { content:'\e0d6'; } .oi[data-glyph=volume-off]:before { content:'\e0d7'; } .oi[data-glyph=warning]:before { content:'\e0d8'; } .oi[data-glyph=wifi]:before { content:'\e0d9'; } .oi[data-glyph=wrench]:before { content:'\e0da'; } .oi[data-glyph=x]:before { content:'\e0db'; } .oi[data-glyph=yen]:before { content:'\e0dc'; } .oi[data-glyph=zoom-in]:before { content:'\e0dd'; } .oi[data-glyph=zoom-out]:before { content:'\e0de'; } sabre-dav-2.1.10/lib/DAV/Browser/assets/openiconic/open-iconic.eot000066400000000000000000000551501267035675400246330ustar00rootroot00000000000000hZYLPbp Icons iconicVersion 1.0.0 Untitled1 PFFTMkLYOS/24QbX`cmap@Bcvt gaspYglyfPJhheadPh6hhea$hmtxlocaͽDmaxp48 namebzRpost3U8cpb_<  χχ"  H"h@.0p0'PfEd H e           X      X  XX         X X   X             XX                            X    X X                                      X     XX   XXXX    X  X        X             X                                X    <  ***Hf4Fh*<Npf 0j|FFv,:HVd"4T| " T t $ > X r ( F p  8 P t  0 F l @x *x,Dbf F|*Hj @`L:j*Nv2@Rfz0>r0H8Rjl*d4fBf , \ x !!D!t!!""B"h""#"#F####$$L$|$$%4./<2<2/<2<233'3#Ҹ !!5!!5!5!, pdpDdddd !!5!!!!', pdpDdddddd  3'354&"4"ddВ͑22hhd  4&"3'3541"͒Вdd͑hh22  !!!!!!!! dXd dXddddddd  !!!!!!!! X Xddddddd  !!!!!!!! X Xddddddd  2%6&54%#%.'!jaH\p p^*Kt1b;2p"&ku408wVSj 33 3d ,    &%# #L,,, L,,    &% 5!5!L,, L    &%!!L,, L,    &%333L L,d X!!%, Xdd X 5!5!, XdX 33 3 ,d !!, )d  5!5!, X  ###)/  ###d   3#3#3#3#dddddddd p," X &&546%'64'&54%'654'wEXXEw?jwwEXXEE;U;;EXHV~|VHuQ7wuHVXDH,)H;RU:;TR;H), 2"&427'vvvvd6\6dd vvvdd    &%"&32654J|8N]Na| J|aN8}]8|a  3!!3#3#dd ,  %163##!"&5#536!"26=4$"26=4dd **** dnd~ddddd !23##!"54!  dd V d ȿ F dpd !23##!"54  dd V  ȿ F  $-46;!2+#!"&54765#"#"&#!/\2"R>>R"2A9z *(;/>RR>-=(  {i   2!476543"&>vBBd;R; vRo5QQ5oR);;& 3#5'7'77772^2KKKKd KKKKddd  %!2#!52654&#!3264&#3264&#Rv,AOh);;),d);;)d>XX> vRF7xJhd;));;R;X|X 3#, , !732!"3!!"&57>7d,dd2&>X+ dd,*dX>&+ !'   !!!35!#!"5 ,)  ddgg   '3232#!"&=4;546353!27#!"5,); D ;)   ;)d  d);ddd X $2#4&#"3#!!5767#53.5476,^jd22/ZWz}27 j^22#;wdagdz WOd G<]?E (,;!2#!"&5546"2647"3!264&#!"D**,XD`****p.'?2#"'>54 ) O6 wnOoDB=I)  6O vO7o&#xI7g46327.>2763232+#"/&=4&#"&'#"&546?&5+"&546;67'.  $IVI$  sD2NA$@?>>&BN2Et  BXM87MXB 8#*" ( #F%"#WR&"&H# ( "#832+%'"&'#"=4;bQ [z"H&'g[  U“ }'  #!2#!"54!5353353335335  V d ddddd ddd  ddddd,dddd  !!!#!"53533533535335D V dddddd ddd d dddddddddd   (032;2#!"546;2?6"264$"264&2"&4^R X>^**~vvvR;;R; >X **vvv;R;;R X! pXp p   p pp X !XpX (046;2!2#!"&'./#"#"&2"&4$2"&4 &Q 6 Q**I**&  H  ))S****  !!#!'!dd,dpdd, dE ./7>O*H"JYEMI'tR*E IZGHK't2 X7 ppXp& p &  '7'p pp2 X ''Xp     &%'LXdHB L8dHB    &777'7'LHHHH L8HHHH %232!546;543!532#!"&546I*K  KKK  v  2 KK 2G v       &$"264%3&/J d"&H$0 JL$!H(0   ,2#54&"!&5463>;26323'3=46h+9 X|XvRJȖ h2U12>XX>213RvVrpȖ  2#'#&5463>#"&=#h+9 vRZ* h2U113RvVr>22d 2#!"&463>h+9X>>RvvRh2U1>XvvVr  2&5462#!"&463>{Mk)220h+9X>>RvvR dr^ #28hȒh2U1>XvvVrd  3#3#'%3#7ddddddddXddddd  '37'#/'7/5?'77"264^d2wE/ ww 2Hw2d2wH2 ww 2Hw|XX|X w 2Hw2d2wH2 ww 2Ht2d2wH2 X|XX|   !!!!! p  dd   3#3#'ddXd  Xp   3#3#ddp  X   !!!!! p  dd'09=FO235462+32"&=#"&46;5#"&4";54$"326435"26=!264&#X|XdX|XX>22>XX|XdX|XX>22>X*2s*2d*,*X>22>XX|XdX|XX>22>XX|XdX| *22*dd*22*  43!2'!"5       &$"264'"264J dddy** JLd,2**     &%264&J| J   !!!!!!!!$2"&4   X** ddddddd**d  !2!546!#!"&5735335    dddd KK   dddd  3!73##5!#53!ddd2dddd ddd^2^ dd2d>ddddd^2  ,4  &$"264$2"&4467;272"&57'&$2"&4J **[);;R;ZI** JL**wZ;R;;)[F**   33 3!!,  ,pd   !!### , dd,d !!%77'7'X^HHHH,HHHHd   #4&"#4%7"&546JddU;;>OvvO||XXiCRvvRCi  !!! !%3535!5,D,dddd pp ddddddd;332#5&+"+#5#"'&=3;26=4&'%.=46;dK1 ,d  17"3264&#"&54," (YE77EY( _*vR); " +ro=||=or+ JRv*;)   !!!  dX  !!,,X ,  3#%3#%3#,,d  !%%! ppddp   !%%5!',,  XX&ddd #2&"!!!#327#"&'!73&5#73>Xc\N™K)ZrV^jl6"2j8oYd-7ddNz8m[d@$d  !!!!!!3#73#73#D  ddddddddddddddddd   !!!!! p  dd   3#3#ddXp  X   3#3#'ddd  Xp   !!!!! p  dd  3#!53!!''7dXdd dd pdd #52#"./>"264'2327"&462cLE1$ OVEG\G ).HMdvvv ;R;;!5@@5 )gOA>XX>9E2"dvvv );;R;""&;632762#"/!'&546K^B+*,,A]> w?ǀ<]A++,|,B^A}?ń< !!! !,D, pp  ,.54>4#!4>4E5++=A- E5+!191 E5+?%!191  8@h8.]GRF\.8@h8(P@BABN8@h8fb(P@BABN   3#!!!5#dd,,p  dd 33337#dȖdB ,  !!!!#!"&5, D ddd> 026;26=.5462+""&54675.54X|X7--7X|X7-X>&.X|X7--7 X>1Mv ;M1>XX>1M;>XH,>XX>1MM1>   !'!7!7'dp dp   7!7'!'dpd pԖd  GU  &%"".&"32676&#".7>'&>54&'&>7&2?&LPI  + !*L &882J*01 D 9#9;   L,#  CG# 6 mC:(,!(   m    .   ''!!apdd,dp  dpdd/dpd #'+/37;?3#73#73#73#3#73#73#73#3#73#73#73#3#73#73#73#ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd    #3#%3#%3#3#%3#%3#3#%3#%3#,,,,,, dd   !!!!!!!!,, ,, ,, !!2#!"&543!27#!"5$"264Xj;** K **3!#"!54&+5!#";!5326=!;!532654&+,2,2,222222dddpdddd %232+"&54&"+"&=46;542duud2|d^SuuSd| 462462 &vvvv;;RvvRRvv;W9  '#5###ddm   !!735'7 ddddd dpddd,  !2#!"543373ddddd dd, X(2>32#"&'#"&46"3267.!"3264&_i3^7]kk]7^33_6]kk]3113?**?s1UU1311Xz:@xx@::@xxd54&"#4R;;R;p|Xd*dX>?W22*d ;R;;RY=%>XV@%qZ > !#3!53>?#z z"N` d ddXA   !!!!!!!!   dXddddddd  !!!!!!!!   Xddddddd  !!!!!!!!   Xddddddd  2#"'#!5&54$"264Вh2UR;;R; Вd2h.;R;;R X ;!23#!"&=35463353dDdPddX^`dd,   !!%3!5!73!5!pdp,dp, ppddpd#=P632#"&5467!632#"&5467!632#"&+.546746;2+"#"&Q    p  j dd     8  * &JP3#53%6#"&54?64'&#"&54?66;26326?632&'&473##dd"7*::V+,4 V,,,5o*;;dd da *:;U,,4U,,*::dd&O6'&'&54632?64'&#"&5476#"'&'&67632&'&4?6K"7*::Ta+* 91V4| 9.V,5o*;;P *:;T 1U4  .U*::P  !!!!!!!!!!!!,p,p,p,,dddd,ddd  #+/2"&47!!2"&47!!2"&47!!2"&47!!**X**X**X**X**dd**dd**dd**d   % pd ,dX 23!3546"354ڤvddT:vRdpdR:*dd*X 23!!54&"#4ڤvd:T:d vRp*::*Rd  "3'346##5265#Suddvd|SudduS|d|duS  !3'35!##!53!#dXddpd2ddd d2d,  5!#5463!!53#!'X d;)pd<( ddd);ddd(XX )gOA~vvv |8~n +}u8|Lvvv! &.!!%46;!!5+"&2.54"264 X|XK%&4( ** dsX>,})(>:J> **dX3#3#XdX XdX2"&4d !p,,d  !pXd3#,,d !3 XdX!!X  !3#!5#3p p  !!!!!!   dddX<632"&=46;263226=46232!46;5.=46);;R;.uu*k2);p;)2k;));;)!:2SuuS22nj;));jn2, !!   $;!2+32!46;5#"&5546!"d);;)dPX d;));d`p 327#"&5469G(Ђ?Ayꦂ#355#3'35#'735#^dddddddd  "&46325"&4632> X|XX>X|XX>Xx >XX|X *s>XX|X "02"&47>72?64'&"2767"&476R;::$gH$@E;&U2344iE}PP9;::$Hg$AE?"R4344gE}PP;  '#5Xddp ddp  *2#"'654'62"&4#54'627!546&>XX>! H.|XX|Xq"),eFF") ) uu(,~VXduuu?&BH>TT?&&?  2"&427!546=uuu YYPoo ΓddtQddQt !2#!"54!"2642d,** d **  26#"'%.54^"e(͑}ag4< s)gjÃQ){F{#46;!2+32!'!46;5#"#"&\2d);8,;)d2 *;)dd,);     &%%L,, L"p  3!!#!5!,,, , 3#2654&/7 &5467,dd>(^В4-%>&?GH> pNKzhh;g#N1S͑R1  !!!2+5!#"54!!p [[ p d    3#3#3#3#3#dddd ddDDXddddd >73#'&'&'#536Ei:H J&3D)T8t VW%%d[ׅXd"1  9462327>32#".'&##47>54&"#34'.8X8   ,88,   8X8  ,88,   8X8   ,88,  X ,02 #54>7>54'&#"'6763#}A<)*!d+*"  &TO%d 1BHdd;6W6_/  6_. !()"%- O1BDd  3G[2.#"."&#"&5463>;2632"&=46;2632"&=46%;2632"&=46h+9?1K. PfP 4-5vR*** h2U13P *4 1??1 &:PRvVr `dddd '5#355#"'5'+5357'5#53276;X502dggd022ddd"&d˿d"& 27!7&#"327#"&6ywmZw||~VEuwwnZXEwJ  !'!pp pp #3 35# ,d X 35 5#%,,X  2#4$#2#4&#2#)Ƞ&ȯ}Su עd}duS  #2#4.#2#4&#2#4&#2"&4dBd_zd|duS)R;;R; zߠ_d戦d|Sud;R;;R  !!2!54&"!#!"&=326546,);p*;) );d*; ;)Ȗ);;)Ȗ&); 3#!53#!"&546!5#">3Kd->--+Fld d}--&-dVC  5#"4>3,d&-mȒȚvG  7"'&'&=6$}%J=@HpH@=J8 n82+ sQ/55/Qs\OzJ$  3#3#3#3#Xdddddddd D , 33###'73,dddddd ddpdd   33'33#!!!!d,p Xddddd   33'3!!!!3#dp, Xddddd #'!2#!"&546353!5353!5353!5K--v--7dddddd---&-ddddddddddddd  !''!d,dd, ,   '/7?G2"&42"&4$2"&42"&42"&4$2"&42"&4$2"&42"&4{******vvv********** **G****Gvvv ********G** !2#!"&546!"264"xP** $d **   ! 6"264, R;;R; ;R;;Rd X 3%3'7$"264,T,Nw***X,Nz**    &$"264$2"&46"264J vvvT::T: JLvvv:T::T  !!!57!'7/ddDdpddd ddpdd  !2#!"547!5  HddHN,  NHddH2dd  !#4&+;!53265#"# 2;)2p2);2 );dd&;)3#!2+"&'&'&#dd^!g/$ m*p%;2#!276763#/4!*m`dd% JIp*!#&#"2654'7 &54675#"&47>7,d2#.)hВ^ͬd* ]d^Вh/,"@=͑ * G  5!5!!!'XXpXddddd '3232!46;46326532653#!"5,d);d);D;)d;d*d*d2 ;);)););^^ 32653+"&5!!d;R;dvR2gdD p);;)pRvuSd  #'32+"54!32+"5432+"54!! a ݶ        pd #+332#54!32#5432#54!!3+"5%3+"5%3+"5 b ޵  , ,  d [[ dd [ [  '!!32+"54!32+"54!32+"54 5 5 dd      d X!273#'#!"&5462dddd Xdd  '3#'#3!52#527>4&'#&3NBB,%&bb#(AUUA +*! d У dkkd 4!"6 X 3#'#33NBB, +*! d 4!"6  3#'#3NBB  32#!"=7635355R& V &ddd VQ Q dd &#"'2&#"'62"&455zrc5KUWK6`LR;;R;mR[^X>U//U>;R;;R!"237#"'#"'&47&546"264'^^g--+)#**^^hS0+hv** >7&''67.'3eS&0fR$3eS&0fR$0fR$3eS&0fR$3eS& 3333!!!#5!5!5!53d&d,d, ,dddddd" '3 #"&/&'#"&$"3276?654%33##5#53",g <*+ gMYВh`@5dddddd ͑VPg +*< g)"iВ;CYh.ddddd" '+ #"&/&'#"&$"3276?654!!",g <*+ gMYВh`@5p, ͑VPg +*< g)"iВ;CYh6dB%< v       J b  Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)IconsIconsiconiciconicFontForge 2.0 : Untitled1 : 30-4-2014FontForge 2.0 : Untitled1 : 30-4-2014Untitled1Untitled1Version 1.0.0 Version 1.0.0 open-iconicopen-iconic2   DEFGHI   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~123456789101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcddde=χχsabre-dav-2.1.10/lib/DAV/Browser/assets/openiconic/open-iconic.otf000066400000000000000000000510701267035675400246310ustar00rootroot00000000000000OTTO  CFF naTIEFFTMkLROS/24Q`cmap BheadPh6hhea$hmtxN~maxpPnamebzppost24 dZ_<  χχ"  H"P0p0'PfEd H e B%< v       J b  Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)IconsIconsiconiciconicFontForge 2.0 : Untitled1 : 30-4-2014FontForge 2.0 : Untitled1 : 30-4-2014Untitled1Untitled1Version 1.0.0 Version 1.0.0 open-iconicopen-iconic<  2 open-iconic: _ _ & #E  "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~   "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`bdfhjlnprtvxz|~123456789101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcddde1.0.0Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)Untitled1IconsBCDEFG      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^#L{ Dh-S} ;^Hx]nm  S  r 5Pp  O#Fh;j;=g"&[ -o;]\Bf[j{.U;zU L !I!!"#$$% %!%_%%&1&x&'3''''((-(B(](q(()i)t)**i*+J+n,,j,--k--..m./ /01112 2M2w23-3344y445 5Q556B66677Y778=8919d9::@::;';;;S\'R'$P>S\'R'$\\ot!zj]=|pxi^eepq|x|]j!uoqxpt,Y6"\6$11#W-9Gais[Wsa9W1'PT^¸v**@@@****@֋u-u'**'*''*'*'\\\\'\'¸^T$T^^T'P61\'¸^TT^^T''*H88HH8*v\\xdlldx8HouuoY'''.v$\\\\P! \$_\\\T^]U'SouPS^T\'\'\\\{{yPy{{\=FMUqi`i`{TQFSY'^qWFF{싋ԮΙF@wË}}ud{ffWQ'$$Px|uwPouPPuoP') \uoouuoou\$v| ~|rtTC b{BPNB(U8;]t|( (OA-WywS{zzFousqoutuv=cv{tvouͮ]dr$uo4ptxh{v=uoGuoIo=; ==:nupxptPuYxJJrCF\>'\'''\''\'$''\''\\P\P>'''\''\''$'''\''\\--J߉!8HH8M9-JMuoouuoou1111'* $$$$.v$$$$$.v$$$$$\ot*r878؏~}Pewz\qwptV+ + v$\P\\$$'P  ZYX!!DB@D!"#***$$$$**v$$$$$**v*****$$$$$****$# \FC@@'$# *F, \\8ouuoY@}}@}@uo'}}}}@\\8$' '- Y'rnnmrstIv\V !5t11fjmH8YSaQ{$x|uw**\\\\**uov$V !5t11fjmr\\\\!aQV\You\VP. dhk#]C9\\. P''\\'\\$\'\\'\Y o CC o Y' YlYC   YY  F YoY YH88HH88H\/ \\\\$! v\$ 0 \\\\\v\0 $ \$\\\! $\\$'/ *P8HH88H'Y8HH88HY8HH8YH88HH8Y'H8'uoYYou$uoouuoY\''*'Youuoouuoouuoouv\\\$' '- \''''*) $$' '::::\P! \'! \" *+ \P}}@}\ } P\''\''v$$'''''VY''V\$\\\*@$pHHppHHppHHppHHp'- @'1 \({0@rT^¸^Tr0{x|uw1 \\\P! vP\! $\\\\\P\\싋V=, $p$PpHHp::::pHHpPe9rP?11P9P3\\P$$$''''''2 $8\P'@FSSFYGR{Jq}Y~~''in|{'`I}w-wstom@\P3 \3 \P4 4 \$P5 \5 \\'\'P\6 \\6 uiio9999oLJuoT^§uoouuo1\$$'/ v\\\\\p7 7 7 P'$\$\\$$$\v$$\$*P**Pe'\'\'\\PN-^@Sr'\iikYx' 9'ԋϠƮV[Fm=2=Tr⋛ߋ͋vhRJFP'PP\'\! \'''\! \\\\\$/ v\$ 0 \$\\v\0 $ \\\\\\\/ $$\\$'! \''\**'**$\'P****'8 'T^^TT^¸{{wou+Px{uw{L J.ŋPS(RRIJ-zo$P$$$v$'$\\'\\\')Z\\\\'\ǽ*\\v$ $\'$\Y\_.v**\I\**\\\\\VouPVv\\\*8HH8ЋITwnPwbTI8HiY\Ɵ‹H88HH8ԋITwPouuo\zy| Ɵ‹H8v9 $: v: *9 $# 'vwsyyy̐fpalh(M]8}ЋwsT?2Z'ehjV\t]of/W}tjuË{uqpxunp~Y~ģS''\_'$$! P''''P\''''P\''''P\''''\\\\\\7 7 7 7 7 7 7 7 7 \\; \; ; \; $ou=*{{yy{{) \\P'uo$ouuoY'You**ouuoY'You$'uo**v\'$\\$P99'Youuo\ou2ou\uoY99TYg000000v\\\$P$$\\\\\'P''''\\''$$''\$\=\'''\''\\\F&((&ڋIS܋F:NSQIRK<'\V]V]\aAq$եU]]qUA`]^.\\\T^^TT^¸^T*8HH8䧡uoo'55K'ouuoou䋯(H8\'7=XY'6XWYP! \! \! P\" P! \! \! \" P! \! \! \" v\\\\{}}Ƌ\\\'< \$x|uw'*ouP*'uo'$''\'$$$$$\= \= 'dJ*v{tvou*uo*$\v{tvou$\uo\*}w{qt\xsuootuo'qwptv$[\['''\\Sdwjj__~w|ouёcdJcc**~w|ou**ً=ojgywy**==>S{Ҥuo|w~__iiEcc̳**uo\=Sdwjj~w|ouёcdJcc**``fqq|outozx}О**ً=ojgywy\{YGyOO**==>S{Ҥuo|w~iiEcc̳**zvv~YP; '$$\'P\; '$$\'P+ *" \+ *" \+ *" \+ *" v$'$\\P11''$$'1'÷_S'\÷$\11÷÷_S\$$$'\1$$$P99'*\*\'*\22'99$\$\'**'*싋\''$\vP'T^^T''\*\*\*''+U11UU11UƋŚ'{}ËË}{'ċU11U""vP$::\\::'8 +\\ou*'싋싋$*ousq%8HH8'****H8') \\\P> \> PP::::::::$P$$$P$$$\P> \P\\Pv\$\$\\\$\\$\\$\\\YP! ! ! '(\**P`h__\T^¸\^T\x|uwY(!s!YT^^T$^TY!(uoouuoY222uo\/ '$Px|uwou''T^^T^T'uoP'$=P2V)DqGGqD) 2c}`^qGGqvP**********'****'**'****'**\Y_'\'''-|yz? j!Ԣ]b|yzd? cPXWwed B BFab_cd_FFFGό̋ceLecyFkkkjkk[[<[[ڋً=dXXv''\\$$$\\$$\$$\'\X_gpWUB6nopH8'8H11H8o`XPlHԦe]YI\:l_'_ul_W::l__VMlFFM_\\$$11e 73'7 UNAd::AU\\\\'b'*Y) v*4dLK!{zz*!YlU11UD$=-TUgA7,6Ulh\ot\'T^^T'^T'\uo$qwpt$# '\\\v\\\\\\\p% x$el;KU(U11UU11UW9eM=l]D;ۯŹ\$\\$\\\\$vPPPPP''\'\'\'\'\{PabaP'443o{=monnAmAnA_^a!v\\\\\\Pows~~wŵŵaQoyws~~wŵaQowys~~ȖaQQaaQ'YP6Lqcccc}Ye͋͋{yytr8ltY_Y_FK rޭˋ z``K8YP'\\$V !5t11WYgܥŻЋЋ[KܜʋdT˜ŋЋaQx|uw\ou\uo`'x|uw'ouuo x|uw'ouuo$P'Yhrvq!B*Bvuh''7S7_$'-R7Rs'\*\*'oCc*O'\*'$PpHHppHHpFUTAj7::::ދhT B(v$::$$::$$::$$::$v\\\\\\\\\\\\v\\\\MM\;;\uu\\2\\'\'rFFrtt\'299\* $$$'\T^^Touuoou*'\T^L¸$uo,*$\,^T$PchhcchV''$\'a:.t[\'\\v\P\$'aZ(\\VTWl:& V_llu':xPnjjehKY v$ 'PP\\v$\'\'\'''\$\'\\v\\@ '\\\'\'$v\\@ '$$\'\'\$Pchhcchhc'''\2 \'''\2 \'''\2 v$'\'\\'\****)$1 3'1 1+ 's& *%+ P!@+ c1 a+ '1 'y{{ypy{ p{y'Y) vP''< \\\  =\\') \\$' '- '& '÷_SS__SS_÷PPP''3!'''\\$$$P=@@@@C'*'\8\¸*ouuoY'$You*¸^T\P% $yxUqq~d*$yvvPPx|w~q*Uyxxyro$uo*ouЋ~qf% '\P'>s'DU11UU11U~-immlurr\wwwwkwwvP'''\*\*\*싋\\T^^T'T^^TP^T'^Tubu'ouuoou'ouuoou\\$1$'$T^^TT^$P'P\\\8PA 6A v\B $! \'\\\\\7PC 7C /u'0\\! \SJSg0ISIS\\\8P! \A B A ouuo$ou*''''*uov$$'CD \u=--K=5uzz{' , tqq\\E v$'D \඗E .v$D >x:>܈>0\'''''\\\\ PfF3 lO;AuMc6NjƋynMC<* +'\\PkloddLdw--\\}}{V) !P!!6988879;6! 87; ;69 !6;9689896v\uu'''\\uu'{e{+\\\\F 'G Y''''''+\\F 'G *\' ス\ #(=DKePX`sz"-5CXvI'$$'P ' ' qGGqqGGqqGGqqGGq  $$ 1111 pHHppHHppHHppHHp uo'ouuoou uoouuoou T^^TT^¸^T ouuoouuo ****C****CC****C**** :::::::: !5t111VaQ \\ \ ouuoouuo $'$   99 22'99 \\\ 1111 $******** ''****$$**  ¸^TT^^TT^¸ '$$ \ 8HH88H \\\\ vJv J S\S \\$\\͋ ja U11UU11UƋŚ${}ËË}{$ċU11U PxUi`~~}cfOuO         X      X  XX         X X   X             XX                            X    X X                                      X     XX   XXXX    X  X        X             X                                X    =χχsabre-dav-2.1.10/lib/DAV/Browser/assets/openiconic/open-iconic.svg000066400000000000000000001533471267035675400246520ustar00rootroot00000000000000 Created by FontForge 20120731 at Wed Apr 30 22:56:47 2014 By P.J. Onori Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) sabre-dav-2.1.10/lib/DAV/Browser/assets/openiconic/open-iconic.ttf000066400000000000000000000617401267035675400246430ustar00rootroot00000000000000pFFTMkLOS/24Qb`cmapxBcvt 22Yfpgm x9Y gaspYglyfJ6headPhL6hheaM,$hmtxMPloca4Pmaxpa R namebzRpost3U@cprepcT=χχ0p0'PfEd H e <  33'3#Ҹ !!5!!5!5!, pdpDdddd !!5!!!!', pdpDdddddd  3'354&"4"ddВ͑22hhd  4&"3'3541"͒Вdd͑hh22  !!!!!!!! dXd dXddddddd  !!!!!!!! X Xddddddd  !!!!!!!! X Xddddddd  2%6&54%#%.'!jaH\p p^*Kt1b;2p"&ku408wVSj 33 3d ,    &%# #L,,, L,,    &% 5!5!L,, L    &%!!L,, L,    &%333L L,d X!!%, Xdd X 5!5!, XdX 33 3 ,d !!, )d  5!5!, X  ###)/  ###d   3#3#3#3#dddddddd p," X &&546%'64'&54%'654'wEXXEw?jwwEXXEE;U;;EXHV~|VHuQ7wuHVXDH,)H;RU:;TR;H), 2"&427'vvvvd6\6dd vvvdd    &%"&32654J|8N]Na| J|aN8}]8|a  3!!3#3#dd ,  %163##!"&5#536!"26=4$"26=4dd **** dnd~ddddd !23##!"54!  dd V d ȿ F dpd !23##!"54  dd V  ȿ F  $-46;!2+#!"&54765#"#"&#!/\2"R>>R"2A9z *(;/>RR>-=(  {i   2!476543"&>vBBd;R; vRo5QQ5oR);;& 3#5'7'77772^2KKKKd KKKKddd  %!2#!52654&#!3264&#3264&#Rv,AOh);;),d);;)d>XX> vRF7xJhd;));;R;X|X 3#, , !732!"3!!"&57>7d,dd2&>X+ dd,*dX>&+ !'   !!!35!#!"5 ,)  ddgg   '3232#!"&=4;546353!27#!"5,); D ;)   ;)d  d);ddd X $2#4&#"3#!!5767#53.5476,^jd22/ZWz}27 j^22#;wdagdz WOd G<]?E (,;!2#!"&5546"2647"3!264&#!"D**,XD`****p.'?2#"'>54 ) O6 wnOoDB=I)  6O vO7o&#xI7g46327.>2763232+#"/&=4&#"&'#"&546?&5+"&546;67'.  $IVI$  sD2NA$@?>>&BN2Et  BXM87MXB 8#*" ( #F%"#WR&"&H# ( "#832+%'"&'#"=4;bQ [z"H&'g[  U“ }'  #!2#!"54!5353353335335  V d ddddd ddd  ddddd,dddd  !!!#!"53533533535335D V dddddd ddd d dddddddddd   (032;2#!"546;2?6"264$"264&2"&4^R X>^**~vvvR;;R; >X **vvv;R;;R X! pXp p   p pp X !XpX (046;2!2#!"&'./#"#"&2"&4$2"&4 &Q 6 Q**I**&  H  ))S****  !!#!'!dd,dpdd, dE ./7>O*H"JYEMI'tR*E IZGHK't2 X7 ppXp& p &  '7'p pp2 X ''Xp     &%'LXdHB L8dHB    &777'7'LHHHH L8HHHH %232!546;543!532#!"&546I*K  KKK  v  2 KK 2G v       &$"264%3&/J d"&H$0 JL$!H(0   ,2#54&"!&5463>;26323'3=46h+9 X|XvRJȖ h2U12>XX>213RvVrpȖ  2#'#&5463>#"&=#h+9 vRZ* h2U113RvVr>22d 2#!"&463>h+9X>>RvvRh2U1>XvvVr  2&5462#!"&463>{Mk)220h+9X>>RvvR dr^ #28hȒh2U1>XvvVrd  3#3#'%3#7ddddddddXddddd  '37'#/'7/5?'77"264^d2wE/ ww 2Hw2d2wH2 ww 2Hw|XX|X w 2Hw2d2wH2 ww 2Ht2d2wH2 X|XX|   !!!!! p  dd   3#3#'ddXd  Xp   3#3#ddp  X   !!!!! p  dd'09=FO235462+32"&=#"&46;5#"&4";54$"326435"26=!264&#X|XdX|XX>22>XX|XdX|XX>22>X*2s*2d*,*X>22>XX|XdX|XX>22>XX|XdX| *22*dd*22*  43!2'!"5       &$"264'"264J dddy** JLd,2**     &%264&J| J   !!!!!!!!$2"&4   X** ddddddd**d  !2!546!#!"&5735335    dddd KK   dddd  3!73##5!#53!ddd2dddd ddd^2^ dd2d>ddddd^2  ,4  &$"264$2"&4467;272"&57'&$2"&4J **[);;R;ZI** JL**wZ;R;;)[F**   33 3!!,  ,pd   !!### , dd,d !!%77'7'X^HHHH,HHHHd   #4&"#4%7"&546JddU;;>OvvO||XXiCRvvRCi  !!! !%3535!5,D,dddd pp ddddddd;332#5&+"+#5#"'&=3;26=4&'%.=46;dK1 ,d  17"3264&#"&54," (YE77EY( _*vR); " +ro=||=or+ JRv*;)   !!!  dX  !!,,X ,  3#%3#%3#,,d  !%%! ppddp   !%%5!',,  XX&ddd #2&"!!!#327#"&'!73&5#73>Xc\N™K)ZrV^jl6"2j8oYd-7ddNz8m[d@$d  !!!!!!3#73#73#D  ddddddddddddddddd   !!!!! p  dd   3#3#ddXp  X   3#3#'ddd  Xp   !!!!! p  dd  3#!53!!''7dXdd dd pdd #52#"./>"264'2327"&462cLE1$ OVEG\G ).HMdvvv ;R;;!5@@5 )gOA>XX>9E2"dvvv );;R;""&;632762#"/!'&546K^B+*,,A]> w?ǀ<]A++,|,B^A}?ń< !!! !,D, pp  ,.54>4#!4>4E5++=A- E5+!191 E5+?%!191  8@h8.]GRF\.8@h8(P@BABN8@h8fb(P@BABN   3#!!!5#dd,,p  dd 33337#dȖdB ,  !!!!#!"&5, D ddd> 026;26=.5462+""&54675.54X|X7--7X|X7-X>&.X|X7--7 X>1Mv ;M1>XX>1M;>XH,>XX>1MM1>   !'!7!7'dp dp   7!7'!'dpd pԖd  GU  &%"".&"32676&#".7>'&>54&'&>7&2?&LPI  + !*L &882J*01 D 9#9;   L,#  CG# 6 mC:(,!(   m    .   ''!!apdd,dp  dpdd/dpd #'+/37;?3#73#73#73#3#73#73#73#3#73#73#73#3#73#73#73#ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd    #3#%3#%3#3#%3#%3#3#%3#%3#,,,,,, dd   !!!!!!!!,, ,, ,, !!2#!"&543!27#!"5$"264Xj;** K **3!#"!54&+5!#";!5326=!;!532654&+,2,2,222222dddpdddd %232+"&54&"+"&=46;542duud2|d^SuuSd| 462462 &vvvv;;RvvRRvv;W9  '#5###ddm   !!735'7 ddddd dpddd,  !2#!"543373ddddd dd, X(2>32#"&'#"&46"3267.!"3264&_i3^7]kk]7^33_6]kk]3113?**?s1UU1311Xz:@xx@::@xxd54&"#4R;;R;p|Xd*dX>?W22*d ;R;;RY=%>XV@%qZ > !#3!53>?#z z"N` d ddXA   !!!!!!!!   dXddddddd  !!!!!!!!   Xddddddd  !!!!!!!!   Xddddddd  2#"'#!5&54$"264Вh2UR;;R; Вd2h.;R;;R X ;!23#!"&=35463353dDdPddX^`dd,   !!%3!5!73!5!pdp,dp, ppddpd#=P632#"&5467!632#"&5467!632#"&+.546746;2+"#"&Q    p  j dd     8  * &JP3#53%6#"&54?64'&#"&54?66;26326?632&'&473##dd"7*::V+,4 V,,,5o*;;dd da *:;U,,4U,,*::dd&O6'&'&54632?64'&#"&5476#"'&'&67632&'&4?6K"7*::Ta+* 91V4| 9.V,5o*;;P *:;T 1U4  .U*::P  !!!!!!!!!!!!,p,p,p,,dddd,ddd  #+/2"&47!!2"&47!!2"&47!!2"&47!!**X**X**X**X**dd**dd**dd**d   % pd ,dX 23!3546"354ڤvddT:vRdpdR:*dd*X 23!!54&"#4ڤvd:T:d vRp*::*Rd  "3'346##5265#Suddvd|SudduS|d|duS  !3'35!##!53!#dXddpd2ddd d2d,  5!#5463!!53#!'X d;)pd<( ddd);ddd(XX )gOA~vvv |8~n +}u8|Lvvv! &.!!%46;!!5+"&2.54"264 X|XK%&4( ** dsX>,})(>:J> **dX3#3#XdX XdX2"&4d !p,,d  !pXd3#,,d !3 XdX!!X  !3#!5#3p p  !!!!!!   dddX<632"&=46;263226=46232!46;5.=46);;R;.uu*k2);p;)2k;));;)!:2SuuS22nj;));jn2, !!   $;!2+32!46;5#"&5546!"d);;)dPX d;));d`p 327#"&5469G(Ђ?Ayꦂ#355#3'35#'735#^dddddddd  "&46325"&4632> X|XX>X|XX>Xx >XX|X *s>XX|X "02"&47>72?64'&"2767"&476R;::$gH$@E;&U2344iE}PP9;::$Hg$AE?"R4344gE}PP;  '#5Xddp ddp  *2#"'654'62"&4#54'627!546&>XX>! H.|XX|Xq"),eFF") ) uu(,~VXduuu?&BH>TT?&&?  2"&427!546=uuu YYPoo ΓddtQddQt !2#!"54!"2642d,** d **  26#"'%.54^"e(͑}ag4< s)gjÃQ){F{#46;!2+32!'!46;5#"#"&\2d);8,;)d2 *;)dd,);     &%%L,, L"p  3!!#!5!,,, , 3#2654&/7 &5467,dd>(^В4-%>&?GH> pNKzhh;g#N1S͑R1  !!!2+5!#"54!!p [[ p d    3#3#3#3#3#dddd ddDDXddddd >73#'&'&'#536Ei:H J&3D)T8t VW%%d[ׅXd"1  9462327>32#".'&##47>54&"#34'.8X8   ,88,   8X8  ,88,   8X8   ,88,  X ,02 #54>7>54'&#"'6763#}A<)*!d+*"  &TO%d 1BHdd;6W6_/  6_. !()"%- O1BDd  3G[2.#"."&#"&5463>;2632"&=46;2632"&=46%;2632"&=46h+9?1K. PfP 4-5vR*** h2U13P *4 1??1 &:PRvVr `dddd '5#355#"'5'+5357'5#53276;X502dggd022ddd"&d˿d"& 27!7&#"327#"&6ywmZw||~VEuwwnZXEwJ  !'!pp pp #3 35# ,d X 35 5#%,,X  2#4$#2#4&#2#)Ƞ&ȯ}Su עd}duS  #2#4.#2#4&#2#4&#2"&4dBd_zd|duS)R;;R; zߠ_d戦d|Sud;R;;R  !!2!54&"!#!"&=326546,);p*;) );d*; ;)Ȗ);;)Ȗ&); 3#!53#!"&546!5#">3Kd->--+Fld d}--&-dVC  5#"4>3,d&-mȒȚvG  7"'&'&=6$}%J=@HpH@=J8 n82+ sQ/55/Qs\OzJ$  3#3#3#3#Xdddddddd D , 33###'73,dddddd ddpdd   33'33#!!!!d,p Xddddd   33'3!!!!3#dp, Xddddd #'!2#!"&546353!5353!5353!5K--v--7dddddd---&-ddddddddddddd  !''!d,dd, ,   '/7?G2"&42"&4$2"&42"&42"&4$2"&42"&4$2"&42"&4{******vvv********** **G****Gvvv ********G** !2#!"&546!"264"xP** $d **   ! 6"264, R;;R; ;R;;Rd X 3%3'7$"264,T,Nw***X,Nz**    &$"264$2"&46"264J vvvT::T: JLvvv:T::T  !!!57!'7/ddDdpddd ddpdd  !2#!"547!5  HddHN,  NHddH2dd  !#4&+;!53265#"# 2;)2p2);2 );dd&;)3#!2+"&'&'&#dd^!g/$ m*p%;2#!276763#/4!*m`dd% JIp*!#&#"2654'7 &54675#"&47>7,d2#.)hВ^ͬd* ]d^Вh/,"@=͑ * G  5!5!!!'XXpXddddd '3232!46;46326532653#!"5,d);d);D;)d;d*d*d2 ;);)););^^ 32653+"&5!!d;R;dvR2gdD p);;)pRvuSd  #'32+"54!32+"5432+"54!! a ݶ        pd #+332#54!32#5432#54!!3+"5%3+"5%3+"5 b ޵  , ,  d [[ dd [ [  '!!32+"54!32+"54!32+"54 5 5 dd      d X!273#'#!"&5462dddd Xdd  '3#'#3!52#527>4&'#&3NBB,%&bb#(AUUA +*! d У dkkd 4!"6 X 3#'#33NBB, +*! d 4!"6  3#'#3NBB  32#!"=7635355R& V &ddd VQ Q dd &#"'2&#"'62"&455zrc5KUWK6`LR;;R;mR[^X>U//U>;R;;R!"237#"'#"'&47&546"264'^^g--+)#**^^hS0+hv** >7&''67.'3eS&0fR$3eS&0fR$0fR$3eS&0fR$3eS& 3333!!!#5!5!5!53d&d,d, ,dddddd" '3 #"&/&'#"&$"3276?654%33##5#53",g <*+ gMYВh`@5dddddd ͑VPg +*< g)"iВ;CYh.ddddd" '+ #"&/&'#"&$"3276?654!!",g <*+ gMYВh`@5p, ͑VPg +*< g)"iВ;CYh6dѷ_<  χχ"  H"          X      X  XX         X X   X             XX                            X    X X                                      X     XX   XXXX    X  X        X             X                                X    0Nj.Pr$6XNpRd.r.^"0>L <d < \ & @ Z  . X  8 \  . T x (``,JNl.d0R(Ht4"R6^(:Nbv&Zv0t :RTLtN|*N  D ` !!,!\!!!"*"P""# #.#l####$4$d$$%hn B%< v       J b  Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)IconsIconsiconiciconicFontForge 2.0 : Untitled1 : 30-4-2014FontForge 2.0 : Untitled1 : 30-4-2014Untitled1Untitled1Version 1.0.0 Version 1.0.0 open-iconicopen-iconic2   DEFGHI   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~123456789101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcddde22, `f-, d P&ZE[X!#!X PPX!@Y 8PX!8YY Ead(PX! E 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY+YY#PXeYY-, E %ad CPX#B#B!!Y`-,#!#! dbB #B *! C +0%QX`PaRYX#Y! @SX+!@Y#PXeY-,C+C`B-,#B# #Bab`*-, E EcEb`D`-, E +#%` E#a d PX!0PX @YY#PXeY%#aDD`-,EaD- ,` CJPX #BY CJRX #BY- , b c#a C` ` #B#- ,KTXDY$ e#x- ,KQXKSXDY!Y$e#x- , CUX CaB +YC%B %B %B# %PXC`%B #a *!#a #a *!C`%B%a *!Y CG CG`b EcEb`#DC>C`B-,ETX #B `a  BB` +m+"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-,+ETX #B `a  BB` +m+"Y-,+-,+-,+-,+-,+-,+- ,+-!,+-",+-#, +-$, <`-%, ` ` C#`C%a`$*!-&,%+%*-', G EcEb`#a8# UX G EcEb`#a8!Y-(,ETX'*0"Y-),+ETX'*0"Y-*, 5`-+,EcEb+EcEb+D>#8**-,, < G EcEb`Ca8--,.<-., < G EcEb`CaCc8-/,% . G#B%IG#G#a Xb!Y#B.*-0,%%G#G#aE+e.# <8-1,%% .G#G#a #BE+ `PX @QX  &YBB# C #G#G#a#F`Cb` + a C`d#CadPXCaC`Y%ba# &#Fa8#CF%CG#G#a` Cb`# +#C`+%a%b&a %`d#%`dPX!#!Y# &#Fa8Y-2, & .G#G#a#<8-3, #B F#G+#a8-4,%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%aEc# Xb!YcEb`#.# <8#!Y-5, C .G#G#a ` `fb# <8-6,# .F%FRX ,1+!# <#B#8&+C.&+-?, G#B.,*-@, G#B.,*-A,-*-B,/*-C,E# . F#a8&+-D,#BC+-E,<+-F,<+-G,<+-H,<+-I,=+-J,=+-K,=+-L,=+-M,9+-N,9+-O,9+-P,9+-Q,;+-R,;+-S,;+-T,;+-U,>+-V,>+-W,>+-X,>+-Y,:+-Z,:+-[,:+-\,:+-],2+.&+-^,2+6+-_,2+7+-`,2+8+-a,3+.&+-b,3+6+-c,3+7+-d,3+8+-e,4+.&+-f,4+6+-g,4+7+-h,4+8+-i,5+.&+-j,5+6+-k,5+7+-l,5+8+-m,+e$Px0-KRXYc #D#pE KQKSZX4(Y`f UX%aEc#b#D * * *Y( ERDD$QX@XD&QXXDYYYYDsabre-dav-2.1.10/lib/DAV/Browser/assets/openiconic/open-iconic.woff000066400000000000000000000301641267035675400250030ustar00rootroot00000000000000wOFF0t YFFTM0kLOS/2LF`4QbcmapBBcvt gaspglyf'Jhhead).6Phhhea)$hmtx)cloca*4ͽDmaxp+ 4name,jbzpost-c3xc```d3΃hO/xc`a8ɀB3603223pÃ{ @LҌHJ$ xc```f`F| @{I*`LF& Ā =yyx{tՙ4e4HdKc%r0qbA:mBX $aӄK}Mk>m@pڔS8}yKe K{|ݜ- };ݖ+F6h\t\d]C? ϊno,A{@,`6?ѷ+ow6.-@>ă<\1"!Aɒ%sn,Vɒ^ʊreǰ¿*| EuZruGT|. s  C;8?Qתr%d B!,Z_qZt%Zw7u^Kq :wX zanF-Ur8-AWĵ&CSނxR\oDI{>"CM/|ܬ _)rs .i_z#zʭ5PcY]i!YC73" |CV0 tDKK^" <}\XÕ^9V\l0>$du àRy%,%VA+B[eo.rߴё%%Ki;Wϻu󧎙wZ,ĭjrin e_7 E|So$$sn:`M<),UGSJ,(VDBo$ |ϥNT*GǹCG1e>>5Q"{K%Hp}7yۻVxr߻'3]c=ӥɮ`2W-\(iȓGO/\Ӝ_C!otw)JJ`wIJ!}}lms[vvߍmyby($]ޗ\9;5{,XX:߁~E\^DX`XH|0φ w#+ GFqɶ'!=[/?t>GjJw1.ݜ>rN:>c.?־x9H#rz`<(qߕt]Z'H/%FQ55K;v&-Z rDž rŦ$W,ɄL@a٥nQB%̥q|;\NeȠ7ZTk\CAY9^<[@‘`M"%[汌uh9" !N@.IQuMVW.q / X0AU^ W\*qϻqɜ*;d~p3 d~/ݺ~$u l@eZQ.>S:/AWU|)c^dh0;8w$g,I6= "rV'!vh䋅"l!I 5mY&SBNp&wѴXuK~{Nu-8[> s:Nk\")YNyĆ A'E-8zί~B?l}:y86"DAm8] RGpgJsosx[f=H%V^\#Hq% YڔT3vqEj!a1T~d+CDZyVmxni@}aMᏨrgӸ*xB@V#<q[%V-^WF)&0> Z&_p{YU|v{RdR} UGQO5˂ xR7, zzi9!*Id9wnIo#e3K엤i){x6B$ =/| b$rЖ@O/+ߒgzq IOBmf>He&ЪY!=P@ 7CéG@n7ՖH}\Ȍg랲 y&iZT'1K&(08 E{(xi+G66(p,W1g] CLPLonջd3gdC92jc1H5?d .k=} aI[32+6WQ6zRmJߜy*A^ԁXwl03œ,@oo2"päQ_i?~c붥r?prMto})xHsA[T$R]^r&QF*"T/ng ߄$ҿْ@®֦DĴƥZpqޠ:,|T*> RӉFY~N/BaKҢ]djkreσ+KjANVL% x)ߑAD'Pꥥ3xԯ7 2}4jkv9,4! "m ]ӜWe?{cYC67"|C36qwRhzZ,鋚lB^H?Uptxwg-mTBw\r;\6R  S}:E&A/P(8:B\P̌kPBhr(=e55O:Gg'JP~;Tw\/CԛfBF6lM5<3ߧπFd߬DY-M# 'V=lGܣ8Z*@xs 8D`V{EM5_ǷͰ\DǹB$:ZH* Һt)Isǡ8M`i5=KJ R-R)EfGd0wD8qc_뭠M#΀ޔhHIwܮG`?2c_pjNpmEapk~籾#lqqN xM K/u!S.SXגeRfh]Rޥ5r`|axñj҇cK3::-Jsr -3]4Rf : ^и:Y'^ucN&P- 6Hk @ň)NH/ܼ5余hnR娏$n!ߴ) x3vn -3jW[3u.VD٭|6 ;bY`F'S<ϋ̖nM;eskȟ3_ylHe5< rb񫯛/lp(7)%}'" !70!wPchZ3ظymKMG֏tv5ivF7e57L߮M[6oao^Fu!1+|Hzc'/N;N311g -&UݨVA= n3`zKAYFjC L$ # .y| Pvb"MWF[ 5Dk|w%];9NB O` *kNDIX:6ddFֆwj4Lq-H=Z̞mkA!Td5r(ĕ,m&d>G?S!oBTR[,6Ij' yUS9tX=M\ڽ;*W ڨlβ'sZ VHʪp)cPfs>|j :>ixh;>jH DR, d>m>]Bi,!7͕qA# XͻL3FB"E%Ȑ5@н7X>g&Ul0-B+BM̐g33:b~GQk_PbIANAC~C$7n#;S;o)d0ɪ侑{G UmzM߰kCnݺT QkQ>f:nJx_=^љ;y *0@T9W|ʾM|S3M7?!I^27N]Θc@CWW!1dB1luXbخԾ:gXv5Gy.&;kyA*&ZWPeo*ԧVCgeӞE_4$u>»4CgE%î?C=U2fBqds,F;/:ZPʿAѵ cpXX^bO;Bes"s906g:2R-5!a94{233ʫmr5h tx0ڎ g{ JBu6qjDJpdIrQXI-+ nrEнHl_#J5[3&7d/b{K%#ydo<'KvUV}^U B4@Thq,jؗ<0VFRSTAFj15V+h@F)k{nTe߹~ ݞ;V@DP!iuc#Щb ǍXcij_"ܐ=.\+ؾ83b1Ctߝ2Z"" EAjwEWi+2זC}m3S)t߶P;eQ̠OAx=cktX;uq[ʰdnlNӘ2lQ51T9?!C-pqnlf5Lm4S [΢/mP ^"!kp=el:pC)XNScC(gzaf0\ϔyoƇo 'M2pXjpx LfAmxuxݍ^ 3@nݷ ćw ؿͫ 0:8;q~ʻ-ν{Z. Ê{nçF_KQA&+/^p=d\nB~ʞ*ZY㕩ޔu?mDߗQ2뢒*>!RU:|:Uf QY( w,mf 뻓Ż.9Gp1d݂;2]ζK[f`3HcQ,/' >8r16m< Mt:#&| 5eݰ)@_, rԏ boEr!SS}b$k6E7OPWz{hj[p2nC1 .>7ԝ϶*e2CmgXhfa~;w:^]XcXI+Oi[T).gzzaPZkű,bzeqZTKKgo{sg/}[|Rǟ女{͡#R,(Iy"4Lf-{N9OAz-3_eJÕ~"u K6$yKtF9~q/@?Q:(ʤ .h <$DPNH?ӥ=MRJ˪ ۼln>cĮ_E"빅=́DS$rs\.r>]$Pj(ǀdcЙN9 [O=(b 'fС˥"&1'2V^g$27waW!u+-e>g&Ϧ ъf^gGcd#?,wޛݧ^=uUq@8"[F6BPBm[2f] )xl4HzA=I?Ez£'n#o1J́Cm?~ Iৗ33C=*Ơ[>`Gu+b(e֓)譟qI Y4PetTG£@ |1n/1ևzHҼs 8wSAC׹ ׎1n_һ~^30B4ȵV(!0YY+92D)Pq P"2\z3F+m<)UupttUjB> /i[E }d y?\1)> 24eӠ6v7d\;&JKIJ.#[&qK nZBQu(nG3ޝ#[V֝<-ʺ;#`B@3V_ܗrIeOXHpjhںӻZ5y[봤: 4;/?vP4CIYT,H"|6_L5EVT2u'i'8VO4 UGpbJr靵!O06z;'GŻn:NM~o-w`2q?4BM A$y֜?Rg~Dgd_=}Tl3C!*Ҭ w[%_$[[O˱/Ye,Kڂ#KL7lgq>,QxǜEk\ v U1Em\8ʎdXƈ^D$(ez ذ6jkr@0 ۫Q.] uU՟8l^zkc ?K˘9!< Nĭ0 5l$PA9 S7MNnxj;wj"I7{8}+7s;=wj{OIJ;:%IlK݆܊eWŨt1 3hxH WsF02O:Ʃ(tlnJY!8B2zlU5jш?gBY}-p/o.tQ?~`4P\ /$FeAM>;îkdkpR{V'hB_RSɕ_܂snulJݎ'?wmĨh}xs,oqx҃ϹWcrӘlK?[^4zy`ȎMи6WD$Y1=-Hf? ;FQ5bfck=cC0Ys7c $z 7D,%)KĿJa0UL>i>紃>hP/t:Jя仵RnYMV\ʥJ66޲t1&Ƃ6saLw@ 4u?fKVcc^A/lzwRcԵd@Aԋ l/DBmp4 %ዛg'pRy7xG(۷\Wb|nj:T uWsI9iS=IK5u .]$2tCx_zXGi&ǃÃMQyxxKխ:$nCZJcgi,R#s1~i+,a"qg'L(,ՠ.LPEFKP&HG Cyeȴv= `F~'͘q!Rxܑ8kmKmf̨$컁*n茫PoCLuw&h۽Iw;om߀둞kfП%ti?蓺^CNWg[c@9`C,+EDiҨsTq^XztV\ Ũje1`㜓)YA ٱe#(V{S';7ݻoW"|@ÊԛϼD ;|gPy^wEzj^G=M`^%sIx"xtx<ʓæ+B%iwH7. <zn nqĴG۴Ww'ɱ{?{TRI-5Ry޲n3tl:M1WD_{e8.1fS֮5T21?t|v7b*88rYHx8tyC}K̾S_^ʚOO|N>5L"YW_o"tv{\BgLҸY4RI;*z045yXn4rOl y:Su:uBƃC 7k ,`?bxc`d``$[x~ @# L QM xc`d``V?PCLvxcT`bF.f)8988sa4z|v"t cݟDLRÍ QakA !BPº8*,***Hf4Fh*<Npf 0j|FFv,:HVd"4T| " T t $ > X r ( F p  8 P t  0 F l @x *x,Dbf F|*Hj @`L:j*Nv2@Rfz0>r0H8Rjl*d4fBf , \ x !!D!t!!""B"h""#"#F####$$L$|$$%4h@.x}=N0ş%P&/He*&*UK{?RDEՍq n`L#Js4 O\B _˸7+{U4ijNRؕ =pOe s1\ťx\ÅxA4fpcq iCqtX,yPW"?`mRf7>x)5饜X $3rgZI2RlK{vL5UՎgaQ\%.bWj=IsS:ZGh=9θ^N궢 88)MKr5SmMbeZR-C-Im{mrxmCeG_}mmwƶn}/9I&m۶mH2'ۤΩUjѿ={dG_se$<|,B,")i,b,,R,2,r, J*jZ:zlFl&lflt,O d+fe;gvd'0iLg3ٙ]ؕ؝=ؓ؛}ؗ؟88C88#8Jc`̄8c8888S88388s88 Y\\l.2. *:n&nn6n.> 0(8OhO4,!1)9_%_5-=?#?3+;Se:#ce 2Q&d2MfLd;Y#ke Q6f"V&;Ȏe'rrN(\\rN[y'|OYUcB`S A!*$PzDS4V)zŠbV,bUlXq)%䔼RPJI)+^*5Ai4QUv^9(G夜r\^}U'QNիըTjQUjSTǪNͨY5ՂZTKjYjU jcN`58 ^C5$ YCkA{sabre-dav-2.1.10/lib/DAV/Browser/assets/sabredav.css000066400000000000000000000056251267035675400220740ustar00rootroot00000000000000/* Start of reset */ * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } body { margin: 0; } /** * Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } /** End of reset */ body { font-family: 'Roboto', sans-serif; font-size: 14px; line-height: 22px; font-weight: 300; } h1 { font-size: 42px; line-height: 44px; padding-bottom: 5px; color: #b10610; margin-top: 10px; margin-bottom: 30px; } h2 { color: #333333; font-size: 28px; line-height: 44px; font-weight: 300; } h3 { font-size: 21px; margin-top: 20px; margin-bottom: 10px; } a { color: #31a1cd; } h1 a { text-decoration: none; } h2 a { color: #333333; } a:visited { color: #6098a2; } h2 a:visited { color: #333333; } a:hover { color: #b10610; } hr { border: none; border-top: 1px dashed #c9ea75; margin-top: 30px; margin-bottom: 30px; } header { background: #eeeeee; } header a { font-size: 28px; font-weight: 500; color: #333; text-decoration: none; } .logo { padding: 5px 10px; } .logo img { vertical-align: middle; } input, button { font: inherit; color: inherit; } input[type=text] { border: 1px solid #bbbbbb; line-height: 22px; padding: 5px 10px; border-radius: 3px; } nav { padding: 5px; } .btn, button, input[type=submit] { display: inline-block; color: white; background: #4fa3ac; padding: 9px 15px; border-radius: 2px; border: 0; text-decoration: none; } a.btn:visited { color: white; } .btn.disabled { background: #eeeeee; color: #bbbbbb; } section { margin: 40px 10px; } section table { height: 40px; } .nodeTable tr { border-bottom: 3px solid white; } .nodeTable td { padding: 10px 10px 10px 10px; } .nodeTable a { text-decoration: none; } .nodeTable .nameColumn { font-weight: bold; padding: 10px 20px; background: #ebf5f6; min-width: 200px; } .nodeTable .oi { color: #b10610; } .propTable tr { height: 40px; } .propTable th { background: #f6f6f6; padding: 0 10px; text-align: left; } .propTable td { padding: 0 10px; background: #eeeeee; } .actions { border: 1px dotted #76baa6; padding: 20px; margin-bottom: 20px; } .actions h3 { margin-top: 10px; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 1px solid #eeeeee; } .actions label { width: 150px; display: inline-block; line-height: 40px; } .actions input[type=text] { width: 450px; } .actions input[type=submit] { display: inline-block; margin-left: 153px; } footer { padding: 50px 0; font-size: 80%; text-align: center; } ul.tree { list-style: none; margin: 0; padding: 0; } ul.tree ul { list-style: none; padding-left: 10px; border-left: 4px solid #ccc; } sabre-dav-2.1.10/lib/DAV/Browser/assets/sabredav.png000066400000000000000000000054111267035675400220610ustar00rootroot00000000000000PNG  IHDR22? IDATh޵{UI T !(P@B,DQ 1Q!H y{{^) o)*!QLsw&3!LUWt{{sO7|ğGfp]'|#:;z\ k*BR>x/~Rp{ SF0Yrm?ìjj 5$EHJD->|S<̓:>A"mBC`H` ?Ӗ=}0m7NוcYè9r]&GhPPa(4KK?'悷P@,ˠV CsUpE{aZ$)SH=W tmj#E2X7³%jV |޻[""@k*ZC$+!yf2UpAJ5@l `J8>f s`WB-3$UG%r8/ |RLp5g:\ cK,V8x&8q0j -exs8 sk4 d2*ډfjEMK[>5[_`qYχvVNjyrBG2hfWUiTL5o~ 9A`Wh`TP-PSI~,AUl"Ji Ѷ7%gKkxIUt0VJnD`Jd.|6 ~g|sT&yr;]MаhWxƮ`\0W0A<̭̈́!: Qq,|/ރM1+wE{zEV#S\l8l8W ,5PDD'/.֩~ (,삱hw kp x+5:v#fix!%Ef ^82'~j3ְB 3/F{P&RZJnY˒ao^&Ue~h*B3nX*NCv޳p9QsjL{^{|x3L[NMKx:fgYHU̖be"PɜÇwݞd d@$oNn?ú)+9b; !|)+-dlPj*JNռJnU78NCSxr2WS: [L[|Nv&_NM NqH%|xK:N]r[*c#X&ݺ9[TCHz፿J"Ihx??̊ZOG&mct0h-5 ֬YZݚQyX%8A/Z?|.:)-8`}  θv ?;Hx!lU/5l%4,81WZXNM|TTj]E:y"&gϰc^Wpt/ۅ&qFHa [ô &iEҔ2ʈZW03#BAٰW̨6UvK8 K>xWj,[zpFi7lF(?.X2k kS ج8i( O5!|or.gipj%A eFM>qmqˢt+UK?(4*m0ƥ.MψbW3;zBȁ,lR]I{V9I/s4~{`j:ݓh͒]FXٯ!l,s{࿶I1.˰%̰]X$J7+p)LlK2o#M(";oP r+Ji^\mjdVl['4PN>w3{ZVÑ}FMWi``뽩2R-$; *,z2<4bm4~C扤{g$$'Upo"$p!X4z3t܂^VwMY<`$[;Fj!|29Hr0qCc)|}yq` x]1EQoCPюT;l~{'eL섇"ҾӐdh} [J>j~:o5b`{I!q-%CZ:rTrM-{e_\KONo 4\~@w>){R9+^ aq\9]lf}l] ڼ5pz0YcJl봩jgky'\Y 7WQryGIENDB`sabre-dav-2.1.10/lib/DAV/Client.php000066400000000000000000000325541267035675400165760ustar00rootroot00000000000000baseUri = $settings['baseUri']; if (isset($settings['proxy'])) { $this->addCurlSetting(CURLOPT_PROXY, $settings['proxy']); } if (isset($settings['userName'])) { $userName = $settings['userName']; $password = isset($settings['password'])?$settings['password']:''; if (isset($settings['authType'])) { $curlType = 0; if ($settings['authType'] & self::AUTH_BASIC) { $curlType |= CURLAUTH_BASIC; } if ($settings['authType'] & self::AUTH_DIGEST) { $curlType |= CURLAUTH_DIGEST; } } else { $curlType = CURLAUTH_BASIC | CURLAUTH_DIGEST; } $this->addCurlSetting(CURLOPT_HTTPAUTH, $curlType); $this->addCurlSetting(CURLOPT_USERPWD, $userName . ':' . $password); } if (isset($settings['encoding'])) { $encoding = $settings['encoding']; $encodings = []; if ($encoding & self::ENCODING_IDENTITY) { $encodings[] = 'identity'; } if ($encoding & self::ENCODING_DEFLATE) { $encodings[] = 'deflate'; } if ($encoding & self::ENCODING_GZIP) { $encodings[] = 'gzip'; } $this->addCurlSetting(CURLOPT_ENCODING, implode(',', $encodings)); } $this->propertyMap['{DAV:}resourcetype'] = 'Sabre\\DAV\\Property\\ResourceType'; } /** * Add trusted root certificates to the webdav client. * * The parameter certificates should be a absolute path to a file * which contains all trusted certificates * * @deprecated This method will be removed in the future, use * addCurlSetting instead. * @param string $certificates * @return void */ function addTrustedCertificates($certificates) { $this->addCurlSetting(CURLOPT_CAINFO, $certificates); } /** * Enables/disables SSL peer verification * * @deprecated This method will be removed in the future, use * addCurlSetting instead. * @param bool $value * @return void */ function setVerifyPeer($value) { $this->addCurlSetting(CURLOPT_SSL_VERIFYPEER, $value); } /** * Does a PROPFIND request * * The list of requested properties must be specified as an array, in clark * notation. * * The returned array will contain a list of filenames as keys, and * properties as values. * * The properties array will contain the list of properties. Only properties * that are actually returned from the server (without error) will be * returned, anything else is discarded. * * Depth should be either 0 or 1. A depth of 1 will cause a request to be * made to the server to also return all child resources. * * @param string $url * @param array $properties * @param int $depth * @return array */ function propFind($url, array $properties, $depth = 0) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $root = $dom->createElementNS('DAV:', 'd:propfind'); $prop = $dom->createElement('d:prop'); foreach($properties as $property) { list( $namespace, $elementName ) = XMLUtil::parseClarkNotation($property); if ($namespace === 'DAV:') { $element = $dom->createElement('d:'.$elementName); } else { $element = $dom->createElementNS($namespace, 'x:'.$elementName); } $prop->appendChild( $element ); } $dom->appendChild($root)->appendChild( $prop ); $body = $dom->saveXML(); $url = $this->getAbsoluteUrl($url); $request = new HTTP\Request('PROPFIND', $url, [ 'Depth' => $depth, 'Content-Type' => 'application/xml' ], $body); $response = $this->send($request); if ((int)$response->getStatus() >= 400) { throw new Exception('HTTP error: ' . $response->getStatus()); } $result = $this->parseMultiStatus($response->getBodyAsString()); // If depth was 0, we only return the top item if ($depth===0) { reset($result); $result = current($result); return isset($result[200])?$result[200]:[]; } $newResult = []; foreach($result as $href => $statusList) { $newResult[$href] = isset($statusList[200])?$statusList[200]:[]; } return $newResult; } /** * Updates a list of properties on the server * * The list of properties must have clark-notation properties for the keys, * and the actual (string) value for the value. If the value is null, an * attempt is made to delete the property. * * @param string $url * @param array $properties * @return void */ function propPatch($url, array $properties) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $root = $dom->createElementNS('DAV:', 'd:propertyupdate'); foreach($properties as $propName => $propValue) { list( $namespace, $elementName ) = XMLUtil::parseClarkNotation($propName); if ($propValue === null) { $remove = $dom->createElement('d:remove'); $prop = $dom->createElement('d:prop'); if ($namespace === 'DAV:') { $element = $dom->createElement('d:'.$elementName); } else { $element = $dom->createElementNS($namespace, 'x:'.$elementName); } $root->appendChild( $remove )->appendChild( $prop )->appendChild( $element ); } else { $set = $dom->createElement('d:set'); $prop = $dom->createElement('d:prop'); if ($namespace === 'DAV:') { $element = $dom->createElement('d:'.$elementName); } else { $element = $dom->createElementNS($namespace, 'x:'.$elementName); } if ( $propValue instanceof Property ) { $propValue->serialize( new Server, $element ); } else { $element->nodeValue = htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8'); } $root->appendChild( $set )->appendChild( $prop )->appendChild( $element ); } } $dom->appendChild($root); $body = $dom->saveXML(); $url = $this->getAbsoluteUrl($url); $request = new HTTP\Request('PROPPATCH', $url, [ 'Content-Type' => 'application/xml', ], $body); $this->send($request); } /** * Performs an HTTP options request * * This method returns all the features from the 'DAV:' header as an array. * If there was no DAV header, or no contents this method will return an * empty array. * * @return array */ function options() { $request = new HTTP\Request('OPTIONS', $this->getAbsoluteUrl('')); $response = $this->send($request); $dav = $response->getHeader('Dav'); if (!$dav) { return []; } $features = explode(',', $dav); foreach($features as &$v) { $v = trim($v); } return $features; } /** * Performs an actual HTTP request, and returns the result. * * If the specified url is relative, it will be expanded based on the base * url. * * The returned array contains 3 keys: * * body - the response body * * httpCode - a HTTP code (200, 404, etc) * * headers - a list of response http headers. The header names have * been lowercased. * * For large uploads, it's highly recommended to specify body as a stream * resource. You can easily do this by simply passing the result of * fopen(..., 'r'). * * This method will throw an exception if an HTTP error was received. Any * HTTP status code above 399 is considered an error. * * Note that it is no longer recommended to use this method, use the send() * method instead. * * @param string $method * @param string $url * @param string|resource|null $body * @param array $headers * @throws ClientException, in case a curl error occurred. * @return array */ function request($method, $url = '', $body = null, array $headers = []) { $url = $this->getAbsoluteUrl($url); $response = $this->send(new HTTP\Request($method, $url, $headers, $body)); return [ 'body' => $response->getBodyAsString(), 'statusCode' => (int)$response->getStatus(), 'headers' => array_change_key_case($response->getHeaders()), ]; } /** * Returns the full url based on the given url (which may be relative). All * urls are expanded based on the base url as given by the server. * * @param string $url * @return string */ function getAbsoluteUrl($url) { // If the url starts with http:// or https://, the url is already absolute. if (preg_match('/^http(s?):\/\//', $url)) { return $url; } // If the url starts with a slash, we must calculate the url based off // the root of the base url. if (strpos($url,'/') === 0) { $parts = parse_url($this->baseUri); return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url; } // Otherwise... return $this->baseUri . $url; } /** * Parses a WebDAV multistatus response body * * This method returns an array with the following structure * * [ * 'url/to/resource' => [ * '200' => [ * '{DAV:}property1' => 'value1', * '{DAV:}property2' => 'value2', * ], * '404' => [ * '{DAV:}property1' => null, * '{DAV:}property2' => null, * ], * ], * 'url/to/resource2' => [ * .. etc .. * ] * ] * * * @param string $body xml body * @return array */ function parseMultiStatus($body) { try { $dom = XMLUtil::loadDOMDocument($body); } catch (Exception\BadRequest $e) { throw new \InvalidArgumentException('The body passed to parseMultiStatus could not be parsed. Is it really xml?'); } $responses = Property\ResponseList::unserialize( $dom->documentElement, $this->propertyMap ); $result = []; foreach($responses->getResponses() as $response) { $result[$response->getHref()] = $response->getResponseProperties(); } return $result; } } sabre-dav-2.1.10/lib/DAV/Collection.php000066400000000000000000000056021267035675400174450ustar00rootroot00000000000000getChildren() as $child) { if ($child->getName()===$name) return $child; } throw new Exception\NotFound('File not found: ' . $name); } /** * Checks is a child-node exists. * * It is generally a good idea to try and override this. Usually it can be optimized. * * @param string $name * @return bool */ function childExists($name) { try { $this->getChild($name); return true; } catch(Exception\NotFound $e) { return false; } } /** * Creates a new file in the directory * * Data will either be supplied as a stream resource, or in certain cases * as a string. Keep in mind that you may have to support either. * * After succesful creation of the file, you may choose to return the ETag * of the new file here. * * The returned ETag must be surrounded by double-quotes (The quotes should * be part of the actual string). * * If you cannot accurately determine the ETag, you should not return it. * If you don't store the file exactly as-is (you're transforming it * somehow) you should also not return an ETag. * * This means that if a subsequent GET to this new file does not exactly * return the same contents of what was submitted here, you are strongly * recommended to omit the ETag. * * @param string $name Name of the file * @param resource|string $data Initial payload * @return null|string */ function createFile($name, $data = null) { throw new Exception\Forbidden('Permission denied to create file (filename ' . $name . ')'); } /** * Creates a new subdirectory * * @param string $name * @throws Exception\Forbidden * @return void */ function createDirectory($name) { throw new Exception\Forbidden('Permission denied to create directory'); } } sabre-dav-2.1.10/lib/DAV/CorePlugin.php000066400000000000000000000751711267035675400174310ustar00rootroot00000000000000server = $server; $server->on('method:GET', [$this, 'httpGet']); $server->on('method:OPTIONS', [$this, 'httpOptions']); $server->on('method:HEAD', [$this, 'httpHead']); $server->on('method:DELETE', [$this, 'httpDelete']); $server->on('method:PROPFIND', [$this, 'httpPropfind']); $server->on('method:PROPPATCH', [$this, 'httpProppatch']); $server->on('method:PUT', [$this, 'httpPut']); $server->on('method:MKCOL', [$this, 'httpMkcol']); $server->on('method:MOVE', [$this, 'httpMove']); $server->on('method:COPY', [$this, 'httpCopy']); $server->on('method:REPORT', [$this, 'httpReport']); $server->on('propPatch', [$this, 'propPatchProtectedPropertyCheck'], 90); $server->on('propPatch', [$this, 'propPatchNodeUpdate'], 200); $server->on('propFind', [$this, 'propFind']); $server->on('propFind', [$this, 'propFindNode'], 120); $server->on('propFind', [$this, 'propFindLate'], 200); } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using DAV\Server::getPlugin * * @return string */ function getPluginName() { return 'core'; } /** * This is the default implementation for the GET method. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpGet(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path,0); if (!$node instanceof IFile) return; $body = $node->get(); // Converting string into stream, if needed. if (is_string($body)) { $stream = fopen('php://temp','r+'); fwrite($stream,$body); rewind($stream); $body = $stream; } /* * TODO: getetag, getlastmodified, getsize should also be used using * this method */ $httpHeaders = $this->server->getHTTPHeaders($path); /* ContentType needs to get a default, because many webservers will otherwise * default to text/html, and we don't want this for security reasons. */ if (!isset($httpHeaders['Content-Type'])) { $httpHeaders['Content-Type'] = 'application/octet-stream'; } if (isset($httpHeaders['Content-Length'])) { $nodeSize = $httpHeaders['Content-Length']; // Need to unset Content-Length, because we'll handle that during figuring out the range unset($httpHeaders['Content-Length']); } else { $nodeSize = null; } $response->addHeaders($httpHeaders); $range = $this->server->getHTTPRange(); $ifRange = $request->getHeader('If-Range'); $ignoreRangeHeader = false; // If ifRange is set, and range is specified, we first need to check // the precondition. if ($nodeSize && $range && $ifRange) { // if IfRange is parsable as a date we'll treat it as a DateTime // otherwise, we must treat it as an etag. try { $ifRangeDate = new \DateTime($ifRange); // It's a date. We must check if the entity is modified since // the specified date. if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; else { $modified = new \DateTime($httpHeaders['Last-Modified']); if($modified > $ifRangeDate) $ignoreRangeHeader = true; } } catch (\Exception $e) { // It's an entity. We can do a simple comparison. if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true; elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true; } } // We're only going to support HTTP ranges if the backend provided a filesize if (!$ignoreRangeHeader && $nodeSize && $range) { // Determining the exact byte offsets if (!is_null($range[0])) { $start = $range[0]; $end = $range[1] ? $range[1] : $nodeSize - 1; if($start >= $nodeSize) throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')'); if($end < $start) throw new Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); if($end >= $nodeSize) $end = $nodeSize - 1; } else { $start = $nodeSize - $range[1]; $end = $nodeSize - 1; if ($start<0) $start = 0; } // New read/write stream $newStream = fopen('php://temp','r+'); // fseek will return 0 only if $streem is seekable (and -1 otherwise) // for a seekable $body stream we set the pointer write before copying it // for a non-seekable $body stream we set the pointer on the copy if ((fseek($body, $start, SEEK_SET)) === 0) { stream_copy_to_stream($body, $newStream, $end - $start + 1, $start); rewind($newStream); } else { stream_copy_to_stream($body, $newStream, $end + 1); fseek($newStream,$start, SEEK_SET); } $response->setHeader('Content-Length', $end - $start + 1); $response->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize); $response->setStatus(206); $response->setBody($newStream); } else { if ($nodeSize) $response->setHeader('Content-Length', $nodeSize); $response->setStatus(200); $response->setBody($body); } // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * HTTP OPTIONS * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpOptions(RequestInterface $request, ResponseInterface $response) { $methods = $this->server->getAllowedMethods($request->getPath()); $response->setHeader('Allow', strtoupper(implode(', ', $methods))); $features = ['1', '3', 'extended-mkcol']; foreach($this->server->getPlugins() as $plugin) { $features = array_merge($features, $plugin->getFeatures()); } $response->setHeader('DAV', implode(', ', $features)); $response->setHeader('MS-Author-Via', 'DAV'); $response->setHeader('Accept-Ranges', 'bytes'); $response->setHeader('Content-Length', '0'); $response->setStatus(200); // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * HTTP HEAD * * This method is normally used to take a peak at a url, and only get the * HTTP response headers, without the body. This is used by clients to * determine if a remote file was changed, so they can use a local cached * version, instead of downloading it again * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpHead(RequestInterface $request, ResponseInterface $response) { // This is implemented by changing the HEAD request to a GET request, // and dropping the response body. $subRequest = clone $request; $subRequest->setMethod('GET'); try { $this->server->invokeMethod($subRequest, $response); $response->setBody(''); } catch (Exception\NotImplemented $e) { // Some clients may do HEAD requests on collections, however, GET // requests and HEAD requests _may_ not be defined on a collection, // which would trigger a 501. // This breaks some clients though, so we're transforming these // 501s into 200s. $response->setStatus(200); $response->setBody(''); $response->setHeader('Content-Type', 'text/plain'); $response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode()); } // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * HTTP Delete * * The HTTP delete method, deletes a given uri * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function httpDelete(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); if (!$this->server->emit('beforeUnbind', [$path])) return false; $this->server->tree->delete($path); $this->server->emit('afterUnbind', [$path]); $response->setStatus(204); $response->setHeader('Content-Length', '0'); // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * WebDAV PROPFIND * * This WebDAV method requests information about an uri resource, or a list of resources * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) * * The request body contains an XML data structure that has a list of properties the client understands * The response body is also an xml document, containing information about every uri resource and the requested properties * * It has to return a HTTP 207 Multi-status status code * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function httpPropfind(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $requestedProperties = $this->server->parsePropFindRequest( $request->getBodyAsString() ); $depth = $this->server->getHTTPDepth(1); // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled if (!$this->server->enablePropfindDepthInfinity && $depth != 0) $depth = 1; $newProperties = $this->server->getPropertiesForPath($path, $requestedProperties, $depth); // This is a multi-status response $response->setStatus(207); $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); $response->setHeader('Vary', 'Brief,Prefer'); // Normally this header is only needed for OPTIONS responses, however.. // iCal seems to also depend on these being set for PROPFIND. Since // this is not harmful, we'll add it. $features = ['1', '3', 'extended-mkcol']; foreach($this->server->getPlugins() as $plugin) { $features = array_merge($features, $plugin->getFeatures()); } $response->setHeader('DAV',implode(', ', $features)); $prefer = $this->server->getHTTPPrefer(); $minimal = $prefer['return-minimal']; $data = $this->server->generateMultiStatus($newProperties, $minimal); $response->setBody($data); // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * WebDAV PROPPATCH * * This method is called to update properties on a Node. The request is an XML body with all the mutations. * In this XML body it is specified which properties should be set/updated and/or deleted * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpPropPatch(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $newProperties = $this->server->parsePropPatchRequest( $request->getBodyAsString() ); $result = $this->server->updateProperties($path, $newProperties); $prefer = $this->server->getHTTPPrefer(); $response->setHeader('Vary', 'Brief,Prefer'); if ($prefer['return-minimal']) { // If return-minimal is specified, we only have to check if the // request was succesful, and don't need to return the // multi-status. $ok = true; foreach($result as $prop=>$code) { if ((int)$code > 299) { $ok = false; } } if ($ok) { $response->setStatus(204); return false; } } $response->setStatus(207); $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); // Reorganizing the result for generateMultiStatus $multiStatus = []; foreach($result as $propertyName => $code) { if (isset($multiStatus[$code])) { $multiStatus[$code][$propertyName] = null; } else { $multiStatus[$code] = [$propertyName => null]; } } $multiStatus['href'] = $path; $response->setBody( $this->server->generateMultiStatus([$multiStatus]) ); // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * HTTP PUT method * * This HTTP method updates a file, or creates a new one. * * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpPut(RequestInterface $request, ResponseInterface $response) { $body = $request->getBodyAsStream(); $path = $request->getPath(); // Intercepting Content-Range if ($request->getHeader('Content-Range')) { /** An origin server that allows PUT on a given target resource MUST send a 400 (Bad Request) response to a PUT request that contains a Content-Range header field. Reference: http://tools.ietf.org/html/rfc7231#section-4.3.4 */ throw new Exception\BadRequest('Content-Range on PUT requests are forbidden.'); } // Intercepting the Finder problem if (($expected = $request->getHeader('X-Expected-Entity-Length')) && $expected > 0) { /** Many webservers will not cooperate well with Finder PUT requests, because it uses 'Chunked' transfer encoding for the request body. The symptom of this problem is that Finder sends files to the server, but they arrive as 0-length files in PHP. If we don't do anything, the user might think they are uploading files successfully, but they end up empty on the server. Instead, we throw back an error if we detect this. The reason Finder uses Chunked, is because it thinks the files might change as it's being uploaded, and therefore the Content-Length can vary. Instead it sends the X-Expected-Entity-Length header with the size of the file at the very start of the request. If this header is set, but we don't get a request body we will fail the request to protect the end-user. */ // Only reading first byte $firstByte = fread($body, 1); if (strlen($firstByte)!==1) { throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); } // The body needs to stay intact, so we copy everything to a // temporary stream. $newBody = fopen('php://temp','r+'); fwrite($newBody, $firstByte); stream_copy_to_stream($body, $newBody); rewind($newBody); $body = $newBody; } if ($this->server->tree->nodeExists($path)) { $node = $this->server->tree->getNodeForPath($path); // If the node is a collection, we'll deny it if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.'); if (!$this->server->updateFile($path, $body, $etag)) { return false; } $response->setHeader('Content-Length','0'); if ($etag) $response->setHeader('ETag', $etag); $response->setStatus(204); } else { $etag = null; // If we got here, the resource didn't exist yet. if (!$this->server->createFile($path, $body, $etag)) { // For one reason or another the file was not created. return false; } $response->setHeader('Content-Length', '0'); if ($etag) $response->setHeader('ETag', $etag); $response->setStatus(201); } // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * WebDAV MKCOL * * The MKCOL method is used to create a new collection (directory) on the server * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpMkcol(RequestInterface $request, ResponseInterface $response) { $requestBody = $request->getBodyAsString(); $path = $request->getPath(); if ($requestBody) { $contentType = $request->getHeader('Content-Type'); if (strpos($contentType, 'application/xml')!==0 && strpos($contentType, 'text/xml')!==0) { // We must throw 415 for unsupported mkcol bodies throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); } $dom = XMLUtil::loadDOMDocument($requestBody); if (XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') { // We must throw 415 for unsupported mkcol bodies throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.'); } $properties = []; foreach($dom->firstChild->childNodes as $childNode) { if (XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue; $properties = array_merge($properties, XMLUtil::parseProperties($childNode, $this->server->propertyMap)); } if (!isset($properties['{DAV:}resourcetype'])) throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property'); $resourceType = $properties['{DAV:}resourcetype']->getValue(); unset($properties['{DAV:}resourcetype']); } else { $properties = []; $resourceType = ['{DAV:}collection']; } $result = $this->server->createCollection($path, $resourceType, $properties); if (is_array($result)) { $response->setStatus(207); $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); $response->setBody( $this->server->generateMultiStatus([$result]) ); } else { $response->setHeader('Content-Length', '0'); $response->setStatus(201); } // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * WebDAV HTTP MOVE method * * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpMove(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $moveInfo = $this->server->getCopyAndMoveInfo($request); if ($moveInfo['destinationExists']) { if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) return false; } if (!$this->server->emit('beforeUnbind',[$path])) return false; if (!$this->server->emit('beforeBind',[$moveInfo['destination']])) return false; if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) return false; if ($moveInfo['destinationExists']) { $this->server->tree->delete($moveInfo['destination']); $this->server->emit('afterUnbind', [$moveInfo['destination']]); } $this->server->tree->move($path, $moveInfo['destination']); // Its important afterMove is called before afterUnbind, because it // allows systems to transfer data from one path to another. // PropertyStorage uses this. If afterUnbind was first, it would clean // up all the properties before it has a chance. $this->server->emit('afterMove', [$path, $moveInfo['destination']]); $this->server->emit('afterUnbind', [$path]); $this->server->emit('afterBind', [$moveInfo['destination']]); // If a resource was overwritten we should send a 204, otherwise a 201 $response->setHeader('Content-Length', '0'); $response->setStatus($moveInfo['destinationExists'] ? 204 : 201); // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * WebDAV HTTP COPY method * * This method copies one uri to a different uri, and works much like the MOVE request * A lot of the actual request processing is done in getCopyMoveInfo * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpCopy(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $copyInfo = $this->server->getCopyAndMoveInfo($request); if ($copyInfo['destinationExists']) { if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) return false; $this->server->tree->delete($copyInfo['destination']); } if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false; $this->server->tree->copy($path, $copyInfo['destination']); $this->server->emit('afterBind', [$copyInfo['destination']]); // If a resource was overwritten we should send a 204, otherwise a 201 $response->setHeader('Content-Length', '0'); $response->setStatus($copyInfo['destinationExists'] ? 204 : 201); // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * HTTP REPORT method implementation * * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) * It's used in a lot of extensions, so it made sense to implement it into the core. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpReport(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $body = $request->getBodyAsString(); $dom = XMLUtil::loadDOMDocument($body); $reportName = XMLUtil::toClarkNotation($dom->firstChild); if ($this->server->emit('report', [$reportName, $dom, $path])) { // If emit returned true, it means the report was not supported throw new Exception\ReportNotSupported(); } // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; } /** * This method is called during property updates. * * Here we check if a user attempted to update a protected property and * ensure that the process fails if this is the case. * * @param string $path * @param PropPatch $propPatch * @return void */ function propPatchProtectedPropertyCheck($path, PropPatch $propPatch) { // Comparing the mutation list to the list of propetected properties. $mutations = $propPatch->getMutations(); $protected = array_intersect( $this->server->protectedProperties, array_keys($mutations) ); if ($protected) { $propPatch->setResultCode($protected, 403); } } /** * This method is called during property updates. * * Here we check if a node implements IProperties and let the node handle * updating of (some) properties. * * @param string $path * @param PropPatch $propPatch * @return void */ function propPatchNodeUpdate($path, PropPatch $propPatch) { // This should trigger a 404 if the node doesn't exist. $node = $this->server->tree->getNodeForPath($path); if ($node instanceof IProperties) { $node->propPatch($propPatch); } } /** * This method is called when properties are retrieved. * * Here we add all the default properties. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { $propFind->handle('{DAV:}getlastmodified', function() use ($node) { $lm = $node->getLastModified(); if ($lm) { return new Property\GetLastModified($lm); } }); if ($node instanceof IFile) { $propFind->handle('{DAV:}getcontentlength', [$node, 'getSize']); $propFind->handle('{DAV:}getetag', [$node, 'getETag']); $propFind->handle('{DAV:}getcontenttype', [$node, 'getContentType']); } if ($node instanceof IQuota) { $quotaInfo = null; $propFind->handle('{DAV:}quota-used-bytes', function() use (&$quotaInfo, $node) { $quotaInfo = $node->getQuotaInfo(); return $quotaInfo[0]; }); $propFind->handle('{DAV:}quota-available-bytes', function() use (&$quotaInfo, $node) { if (!$quotaInfo) { $quotaInfo = $node->getQuotaInfo(); } return $quotaInfo[1]; }); } $propFind->handle('{DAV:}supported-report-set', function() use ($propFind) { $reports = []; foreach($this->server->getPlugins() as $plugin) { $reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath())); } return new Property\SupportedReportSet($reports); }); $propFind->handle('{DAV:}resourcetype', function() use ($node) { return new Property\ResourceType($this->server->getResourceTypeForNode($node)); }); $propFind->handle('{DAV:}supported-method-set', function() use ($propFind) { return new Property\SupportedMethodSet( $this->server->getAllowedMethods($propFind->getPath()) ); }); } /** * Fetches properties for a node. * * This event is called a bit later, so plugins have a chance first to * populate the result. * * @param PropFind $propFind * @param INode $node * @return void */ function propFindNode(PropFind $propFind, INode $node) { if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) { $nodeProperties = $node->getProperties($propertyNames); foreach($nodeProperties as $propertyName=>$value) { $propFind->set($propertyName, $value, 200); } } } /** * This method is called when properties are retrieved. * * This specific handler is called very late in the process, because we * want other systems to first have a chance to handle the properties. * * @param PropFind $propFind * @param INode $node * @return void */ function propFindLate(PropFind $propFind, INode $node) { $propFind->handle('{http://calendarserver.org/ns/}getctag', function() use ($propFind) { // If we already have a sync-token from the current propFind // request, we can re-use that. $val = $propFind->get('{http://sabredav.org/ns}sync-token'); if ($val) return $val; $val = $propFind->get('{DAV:}sync-token'); if ($val && is_scalar($val)) { return $val; } if ($val && $val instanceof Property\IHref) { return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); } // If we got here, the earlier two properties may simply not have // been part of the earlier request. We're going to fetch them. $result = $this->server->getProperties($propFind->getPath(), [ '{http://sabredav.org/ns}sync-token', '{DAV:}sync-token', ]); if (isset($result['{http://sabredav.org/ns}sync-token'])) { return $result['{http://sabredav.org/ns}sync-token']; } if (isset($result['{DAV:}sync-token'])) { $val = $result['{DAV:}sync-token']; if (is_scalar($val)) { return $val; } elseif ($val instanceof Property\IHref) { return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); } } }); } } sabre-dav-2.1.10/lib/DAV/Exception.php000066400000000000000000000026241267035675400173110ustar00rootroot00000000000000lock) { $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock'); $errorNode->appendChild($error); $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); } } } sabre-dav-2.1.10/lib/DAV/Exception/FileNotFound.php000066400000000000000000000007431267035675400216450ustar00rootroot00000000000000ownerDocument->createElementNS('DAV:','d:valid-resourcetype'); $errorNode->appendChild($error); } } sabre-dav-2.1.10/lib/DAV/Exception/InvalidSyncToken.php000066400000000000000000000017701267035675400225360ustar00rootroot00000000000000ownerDocument->createElementNS('DAV:','d:valid-sync-token'); $errorNode->appendChild($error); } } sabre-dav-2.1.10/lib/DAV/Exception/LengthRequired.php000066400000000000000000000010721267035675400222270ustar00rootroot00000000000000message = 'The locktoken supplied does not match any locks on this entity'; } /** * This method allows the exception to include additional information into the WebDAV error response * * @param DAV\Server $server * @param \DOMElement $errorNode * @return void */ function serialize(DAV\Server $server,\DOMElement $errorNode) { $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri'); $errorNode->appendChild($error); } } sabre-dav-2.1.10/lib/DAV/Exception/Locked.php000066400000000000000000000032371267035675400205130ustar00rootroot00000000000000lock = $lock; } /** * Returns the HTTP statuscode for this exception * * @return int */ function getHTTPCode() { return 423; } /** * This method allows the exception to include additional information into the WebDAV error response * * @param DAV\Server $server * @param \DOMElement $errorNode * @return void */ function serialize(DAV\Server $server,\DOMElement $errorNode) { if ($this->lock) { $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted'); $errorNode->appendChild($error); $href = $errorNode->ownerDocument->createElementNS('DAV:','d:href'); $href->appendChild($errorNode->ownerDocument->createTextNode($this->lock->uri)); $error->appendChild( $href ); } } } sabre-dav-2.1.10/lib/DAV/Exception/MethodNotAllowed.php000066400000000000000000000017401267035675400225200ustar00rootroot00000000000000getAllowedMethods($server->getRequestUri()); return [ 'Allow' => strtoupper(implode(', ',$methods)), ]; } } sabre-dav-2.1.10/lib/DAV/Exception/NotAuthenticated.php000066400000000000000000000010501267035675400225440ustar00rootroot00000000000000header = $header; } /** * Returns the HTTP statuscode for this exception * * @return int */ function getHTTPCode() { return 412; } /** * This method allows the exception to include additional information into the WebDAV error response * * @param DAV\Server $server * @param \DOMElement $errorNode * @return void */ function serialize(DAV\Server $server,\DOMElement $errorNode) { if ($this->header) { $prop = $errorNode->ownerDocument->createElement('s:header'); $prop->nodeValue = $this->header; $errorNode->appendChild($prop); } } } sabre-dav-2.1.10/lib/DAV/Exception/ReportNotSupported.php000066400000000000000000000015041267035675400231470ustar00rootroot00000000000000ownerDocument->createElementNS('DAV:','d:supported-report'); $errorNode->appendChild($error); } } sabre-dav-2.1.10/lib/DAV/Exception/RequestedRangeNotSatisfiable.php000066400000000000000000000011101267035675400250440ustar00rootroot00000000000000 * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @license http://sabre.io/license/ Modified BSD License */ class ServiceUnavailable extends DAV\Exception { /** * Returns the HTTP statuscode for this exception * * @return int */ function getHTTPCode() { return 503; } } sabre-dav-2.1.10/lib/DAV/Exception/TooManyMatches.php000066400000000000000000000020611267035675400221770ustar00rootroot00000000000000ownerDocument->createElementNS('DAV:','d:number-of-matches-within-limits'); $errorNode->appendChild($error); } } sabre-dav-2.1.10/lib/DAV/Exception/UnsupportedMediaType.php000066400000000000000000000011451267035675400234400ustar00rootroot00000000000000path . '/' . $name; file_put_contents($newPath,$data); } /** * Creates a new subdirectory * * @param string $name * @return void */ function createDirectory($name) { $newPath = $this->path . '/' . $name; mkdir($newPath); } /** * Returns a specific child node, referenced by its name * * This method must throw DAV\Exception\NotFound if the node does not * exist. * * @param string $name * @throws DAV\Exception\NotFound * @return DAV\INode */ function getChild($name) { $path = $this->path . '/' . $name; if (!file_exists($path)) throw new DAV\Exception\NotFound('File with name ' . $path . ' could not be located'); if (is_dir($path)) { return new Directory($path); } else { return new File($path); } } /** * Returns an array with all the child nodes * * @return DAV\INode[] */ function getChildren() { $nodes = []; foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node); return $nodes; } /** * Checks if a child exists. * * @param string $name * @return bool */ function childExists($name) { $path = $this->path . '/' . $name; return file_exists($path); } /** * Deletes all files in this directory, and then itself * * @return void */ function delete() { foreach($this->getChildren() as $child) $child->delete(); rmdir($this->path); } /** * Returns available diskspace information * * @return array */ function getQuotaInfo() { return [ disk_total_space($this->path)-disk_free_space($this->path), disk_free_space($this->path) ]; } } sabre-dav-2.1.10/lib/DAV/FS/File.php000066400000000000000000000030011267035675400165300ustar00rootroot00000000000000path,$data); } /** * Returns the data * * @return string */ function get() { return fopen($this->path,'r'); } /** * Delete the current file * * @return void */ function delete() { unlink($this->path); } /** * Returns the size of the node, in bytes * * @return int */ function getSize() { return filesize($this->path); } /** * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. * * Return null if the ETag can not effectively be determined * * @return mixed */ function getETag() { return null; } /** * Returns the mime-type for a file * * If null is returned, we'll assume application/octet-stream * * @return mixed */ function getContentType() { return null; } } sabre-dav-2.1.10/lib/DAV/FS/Node.php000066400000000000000000000026471267035675400165550ustar00rootroot00000000000000path = $path; } /** * Returns the name of the node * * @return string */ function getName() { list(, $name) = URLUtil::splitPath($this->path); return $name; } /** * Renames the node * * @param string $name The new name * @return void */ function setName($name) { list($parentPath, ) = URLUtil::splitPath($this->path); list(, $newName) = URLUtil::splitPath($name); $newPath = $parentPath . '/' . $newName; rename($this->path,$newPath); $this->path = $newPath; } /** * Returns the last modification time, as a unix timestamp * * @return int */ function getLastModified() { return filemtime($this->path); } } sabre-dav-2.1.10/lib/DAV/FSExt/000077500000000000000000000000001267035675400156275ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAV/FSExt/Directory.php000066400000000000000000000125101267035675400203030ustar00rootroot00000000000000path . '/' . $name; file_put_contents($newPath,$data); return '"' . md5_file($newPath) . '"'; } /** * Creates a new subdirectory * * @param string $name * @return void */ function createDirectory($name) { // We're not allowing dots if ($name=='.' || $name=='..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); $newPath = $this->path . '/' . $name; mkdir($newPath); } /** * Returns a specific child node, referenced by its name * * This method must throw Sabre\DAV\Exception\NotFound if the node does not * exist. * * @param string $name * @throws DAV\Exception\NotFound * @return DAV\INode */ function getChild($name) { $path = $this->path . '/' . $name; if (!file_exists($path)) throw new DAV\Exception\NotFound('File could not be located'); if ($name=='.' || $name=='..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); if (is_dir($path)) { return new Directory($path); } else { return new File($path); } } /** * Checks if a child exists. * * @param string $name * @return bool */ function childExists($name) { if ($name=='.' || $name=='..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); $path = $this->path . '/' . $name; return file_exists($path); } /** * Returns an array with all the child nodes * * @return DAV\INode[] */ function getChildren() { $nodes = []; foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node); return $nodes; } /** * Deletes all files in this directory, and then itself * * @return bool */ function delete() { // Deleting all children foreach($this->getChildren() as $child) $child->delete(); // Removing resource info, if its still around if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav'); // Removing the directory itself rmdir($this->path); return parent::delete(); } /** * Returns available diskspace information * * @return array */ function getQuotaInfo() { return [ disk_total_space($this->path)-disk_free_space($this->path), disk_free_space($this->path) ]; } /** * Moves a node into this collection. * * It is up to the implementors to: * 1. Create the new resource. * 2. Remove the old resource. * 3. Transfer any properties or other data. * * Generally you should make very sure that your collection can easily move * the move. * * If you don't, just return false, which will trigger sabre/dav to handle * the move itself. If you return true from this function, the assumption * is that the move was successful. * * @param string $targetName New local file/collection name. * @param string $sourcePath Full path to source node * @param DAV\INode $sourceNode Source node itself * @return bool */ function moveInto($targetName, $sourcePath, DAV\INode $sourceNode) { // We only support FSExt\Directory or FSExt\File objects, so // anything else we want to quickly reject. if (!$sourceNode instanceof Node) { return false; } // PHP allows us to access protected properties from other objects, as // long as they are defined in a class that has a shared inheritence // with the current class. rename($sourceNode->path, $this->path . '/' . $targetName); return true; } } sabre-dav-2.1.10/lib/DAV/FSExt/File.php000066400000000000000000000064151267035675400172250ustar00rootroot00000000000000path,$data); return '"' . md5_file($this->path) . '"'; } /** * Updates the file based on a range specification. * * The first argument is the data, which is either a readable stream * resource or a string. * * The second argument is the type of update we're doing. * This is either: * * 1. append * * 2. update based on a start byte * * 3. update based on an end byte *; * The third argument is the start or end byte. * * After a successful put operation, you may choose to return an ETag. The * etag must always be surrounded by double-quotes. These quotes must * appear in the actual string you're returning. * * Clients may use the ETag from a PUT request to later on make sure that * when they update the file, the contents haven't changed in the mean * time. * * @param resource|string $data * @param int $rangeType * @param int $offset * @return string|null */ function patch($data, $rangeType, $offset = null) { switch($rangeType) { case 1 : $f = fopen($this->path, 'a'); break; case 2 : $f = fopen($this->path, 'c'); fseek($f,$offset); break; case 3 : $f = fopen($this->path, 'c'); fseek($f, $offset, SEEK_END); break; } if (is_string($data)) { fwrite($f, $data); } else { stream_copy_to_stream($data,$f); } fclose($f); return '"' . md5_file($this->path) . '"'; } /** * Returns the data * * @return resource */ function get() { return fopen($this->path,'r'); } /** * Delete the current file * * @return bool */ function delete() { return unlink($this->path) && parent::delete(); } /** * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. * * Return null if the ETag can not effectively be determined * * @return string|null */ function getETag() { return '"' . md5_file($this->path). '"'; } /** * Returns the mime-type for a file * * If null is returned, we'll assume application/octet-stream * * @return string|null */ function getContentType() { return null; } /** * Returns the size of the file, in bytes * * @return int */ function getSize() { return filesize($this->path); } } sabre-dav-2.1.10/lib/DAV/FSExt/Node.php000066400000000000000000000135741267035675400172370ustar00rootroot00000000000000handleRemaining(function(array $properties) { $resourceData = $this->getResourceData(); foreach($properties as $propertyName=>$propertyValue) { // If it was null, we need to delete the property if (is_null($propertyValue)) { unset($resourceData['properties'][$propertyName]); } else { $resourceData['properties'][$propertyName] = $propertyValue; } } $this->putResourceData($resourceData); return true; }); } /** * Returns a list of properties for this nodes. * * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author * If the array is empty, all properties should be returned. * * @param array $properties * @return array */ function getProperties($properties) { $resourceData = $this->getResourceData(); // if the array was empty, we need to return everything if (!$properties) return $resourceData['properties']; $props = []; foreach($properties as $property) { if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property]; } return $props; } /** * Returns the path to the resource file. * * @return string */ protected function getResourceInfoPath() { list($parentDir) = URLUtil::splitPath($this->path); return $parentDir . '/.sabredav'; } /** * Returns all the stored resource information. * * @return array */ protected function getResourceData() { $path = $this->getResourceInfoPath(); if (!file_exists($path)) return ['properties' => []]; // opening up the file, and creating a shared lock $handle = fopen($path,'r'); flock($handle,LOCK_SH); $data = ''; // Reading data until the eof while(!feof($handle)) { $data.=fread($handle,8192); } // We're all good flock($handle,LOCK_UN); fclose($handle); // Unserializing and checking if the resource file contains data for this file $data = unserialize($data); if (!isset($data[$this->getName()])) { return ['properties' => []]; } $data = $data[$this->getName()]; if (!isset($data['properties'])) $data['properties'] = []; return $data; } /** * Updates the resource information. * * @param array $newData * @return void */ protected function putResourceData(array $newData) { $path = $this->getResourceInfoPath(); // opening up the file, and creating a shared lock $handle = fopen($path,'a+'); flock($handle,LOCK_EX); $data = ''; rewind($handle); // Reading data until the eof while(!feof($handle)) { $data.=fread($handle,8192); } // Unserializing and checking if the resource file contains data for this file $data = unserialize($data); $data[$this->getName()] = $newData; ftruncate($handle,0); rewind($handle); fwrite($handle,serialize($data)); flock($handle,LOCK_UN); fclose($handle); } /** * Renames the node. * * @param string $name The new name * @return void */ function setName($name) { list($parentPath, ) = URLUtil::splitPath($this->path); list(, $newName) = URLUtil::splitPath($name); $newPath = $parentPath . '/' . $newName; // We're deleting the existing resourcedata, and recreating it // for the new path. $resourceData = $this->getResourceData(); $this->deleteResourceData(); rename($this->path,$newPath); $this->path = $newPath; $this->putResourceData($resourceData); } /** * Deletes the resource information. * @return bool */ protected function deleteResourceData() { // When we're deleting this node, we also need to delete any resource information $path = $this->getResourceInfoPath(); if (!file_exists($path)) return true; // opening up the file, and creating a shared lock $handle = fopen($path,'a+'); flock($handle,LOCK_EX); $data = ''; rewind($handle); // Reading data until the eof while(!feof($handle)) { $data.=fread($handle,8192); } // Unserializing and checking if the resource file contains data for this file $data = unserialize($data); unset($data[$this->getName()]); ftruncate($handle,0); rewind($handle); fwrite($handle,serialize($data)); flock($handle,LOCK_UN); fclose($handle); return true; } function delete() { return $this->deleteResourceData(); } } sabre-dav-2.1.10/lib/DAV/File.php000066400000000000000000000033101267035675400162230ustar00rootroot00000000000000dataDir = $dataDir; } protected function getFileNameForUri($uri) { return $this->dataDir . '/sabredav_' . md5($uri) . '.locks'; } /** * Returns a list of Sabre\DAV\Locks\LockInfo objects * * This method should return all the locks for a particular uri, including * locks that might be set on a parent uri. * * If returnChildLocks is set to true, this method should also look for * any locks in the subtree of the uri for locks. * * @param string $uri * @param bool $returnChildLocks * @return array */ function getLocks($uri, $returnChildLocks) { $lockList = []; $currentPath = ''; foreach(explode('/',$uri) as $uriPart) { // weird algorithm that can probably be improved, but we're traversing the path top down if ($currentPath) $currentPath.='/'; $currentPath.=$uriPart; $uriLocks = $this->getData($currentPath); foreach($uriLocks as $uriLock) { // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0 if($uri==$currentPath || $uriLock->depth!=0) { $uriLock->uri = $currentPath; $lockList[] = $uriLock; } } } // Checking if we can remove any of these locks foreach($lockList as $k=>$lock) { if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); } return $lockList; } /** * Locks a uri * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function lock($uri, LockInfo $lockInfo) { // We're making the lock timeout 30 minutes $lockInfo->timeout = 1800; $lockInfo->created = time(); $locks = $this->getLocks($uri,false); foreach($locks as $k=>$lock) { if ($lock->token == $lockInfo->token) unset($locks[$k]); } $locks[] = $lockInfo; $this->putData($uri,$locks); return true; } /** * Removes a lock from a uri * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function unlock($uri, LockInfo $lockInfo) { $locks = $this->getLocks($uri,false); foreach($locks as $k=>$lock) { if ($lock->token == $lockInfo->token) { unset($locks[$k]); $this->putData($uri,$locks); return true; } } return false; } /** * Returns the stored data for a uri * * @param string $uri * @return array */ protected function getData($uri) { $path = $this->getFilenameForUri($uri); if (!file_exists($path)) return []; // opening up the file, and creating a shared lock $handle = fopen($path,'r'); flock($handle,LOCK_SH); $data = ''; // Reading data until the eof while(!feof($handle)) { $data.=fread($handle,8192); } // We're all good flock($handle,LOCK_UN); fclose($handle); // Unserializing and checking if the resource file contains data for this file $data = unserialize($data); if (!$data) return []; return $data; } /** * Updates the lock information * * @param string $uri * @param array $newData * @return void */ protected function putData($uri,array $newData) { $path = $this->getFileNameForUri($uri); // opening up the file, and creating a shared lock $handle = fopen($path,'a+'); flock($handle,LOCK_EX); ftruncate($handle,0); rewind($handle); fwrite($handle,serialize($newData)); flock($handle,LOCK_UN); fclose($handle); } } sabre-dav-2.1.10/lib/DAV/Locks/Backend/File.php000066400000000000000000000105211267035675400206270ustar00rootroot00000000000000locksFile = $locksFile; } /** * Returns a list of Sabre\DAV\Locks\LockInfo objects * * This method should return all the locks for a particular uri, including * locks that might be set on a parent uri. * * If returnChildLocks is set to true, this method should also look for * any locks in the subtree of the uri for locks. * * @param string $uri * @param bool $returnChildLocks * @return array */ function getLocks($uri, $returnChildLocks) { $newLocks = []; $locks = $this->getData(); foreach($locks as $lock) { if ($lock->uri === $uri || //deep locks on parents ($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) || // locks on children ($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) { $newLocks[] = $lock; } } // Checking if we can remove any of these locks foreach($newLocks as $k=>$lock) { if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); } return $newLocks; } /** * Locks a uri * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function lock($uri, LockInfo $lockInfo) { // We're making the lock timeout 30 minutes $lockInfo->timeout = 1800; $lockInfo->created = time(); $lockInfo->uri = $uri; $locks = $this->getData(); foreach($locks as $k=>$lock) { if ( ($lock->token == $lockInfo->token) || (time() > $lock->timeout + $lock->created) ) { unset($locks[$k]); } } $locks[] = $lockInfo; $this->putData($locks); return true; } /** * Removes a lock from a uri * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function unlock($uri, LockInfo $lockInfo) { $locks = $this->getData(); foreach($locks as $k=>$lock) { if ($lock->token == $lockInfo->token) { unset($locks[$k]); $this->putData($locks); return true; } } return false; } /** * Loads the lockdata from the filesystem. * * @return array */ protected function getData() { if (!file_exists($this->locksFile)) return []; // opening up the file, and creating a shared lock $handle = fopen($this->locksFile,'r'); flock($handle,LOCK_SH); // Reading data until the eof $data = stream_get_contents($handle); // We're all good flock($handle,LOCK_UN); fclose($handle); // Unserializing and checking if the resource file contains data for this file $data = unserialize($data); if (!$data) return []; return $data; } /** * Saves the lockdata * * @param array $newData * @return void */ protected function putData(array $newData) { // opening up the file, and creating an exclusive lock $handle = fopen($this->locksFile,'a+'); flock($handle,LOCK_EX); // We can only truncate and rewind once the lock is acquired. ftruncate($handle,0); rewind($handle); fwrite($handle,serialize($newData)); flock($handle,LOCK_UN); fclose($handle); } } sabre-dav-2.1.10/lib/DAV/Locks/Backend/PDO.php000066400000000000000000000115551267035675400204020ustar00rootroot00000000000000pdo = $pdo; $this->tableName = $tableName; } /** * Returns a list of Sabre\DAV\Locks\LockInfo objects * * This method should return all the locks for a particular uri, including * locks that might be set on a parent uri. * * If returnChildLocks is set to true, this method should also look for * any locks in the subtree of the uri for locks. * * @param string $uri * @param bool $returnChildLocks * @return array */ function getLocks($uri, $returnChildLocks) { // NOTE: the following 10 lines or so could be easily replaced by // pure sql. MySQL's non-standard string concatenation prevents us // from doing this though. $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)'; $params = [time(),$uri]; // We need to check locks for every part in the uri. $uriParts = explode('/',$uri); // We already covered the last part of the uri array_pop($uriParts); $currentPath=''; foreach($uriParts as $part) { if ($currentPath) $currentPath.='/'; $currentPath.=$part; $query.=' OR (depth!=0 AND uri = ?)'; $params[] = $currentPath; } if ($returnChildLocks) { $query.=' OR (uri LIKE ?)'; $params[] = $uri . '/%'; } $query.=')'; $stmt = $this->pdo->prepare($query); $stmt->execute($params); $result = $stmt->fetchAll(); $lockList = []; foreach($result as $row) { $lockInfo = new LockInfo(); $lockInfo->owner = $row['owner']; $lockInfo->token = $row['token']; $lockInfo->timeout = $row['timeout']; $lockInfo->created = $row['created']; $lockInfo->scope = $row['scope']; $lockInfo->depth = $row['depth']; $lockInfo->uri = $row['uri']; $lockList[] = $lockInfo; } return $lockList; } /** * Locks a uri * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function lock($uri, LockInfo $lockInfo) { // We're making the lock timeout 30 minutes $lockInfo->timeout = 30*60; $lockInfo->created = time(); $lockInfo->uri = $uri; $locks = $this->getLocks($uri,false); $exists = false; foreach($locks as $lock) { if ($lock->token == $lockInfo->token) $exists = true; } if ($exists) { $stmt = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); $stmt->execute([ $lockInfo->owner, $lockInfo->timeout, $lockInfo->scope, $lockInfo->depth, $uri, $lockInfo->created, $lockInfo->token ]); } else { $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); $stmt->execute([ $lockInfo->owner, $lockInfo->timeout, $lockInfo->scope, $lockInfo->depth, $uri, $lockInfo->created, $lockInfo->token ]); } return true; } /** * Removes a lock from a uri * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function unlock($uri, LockInfo $lockInfo) { $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE uri = ? AND token = ?'); $stmt->execute([$uri, $lockInfo->token]); return $stmt->rowCount()===1; } } sabre-dav-2.1.10/lib/DAV/Locks/LockInfo.php000066400000000000000000000024101267035675400201230ustar00rootroot00000000000000addPlugin($lockPlugin); * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class Plugin extends DAV\ServerPlugin { /** * locksBackend * * @var Backend\Backend\Interface */ protected $locksBackend; /** * server * * @var Sabre\DAV\Server */ protected $server; /** * __construct * * @param Backend\BackendInterface $locksBackend */ function __construct(Backend\BackendInterface $locksBackend = null) { $this->locksBackend = $locksBackend; } /** * Initializes the plugin * * This method is automatically called by the Server class after addPlugin. * * @param DAV\Server $server * @return void */ function initialize(DAV\Server $server) { $this->server = $server; $server->on('method:LOCK', [$this, 'httpLock']); $server->on('method:UNLOCK', [$this, 'httpUnlock']); $server->on('validateTokens', [$this, 'validateTokens']); $server->on('propFind', [$this, 'propFind']); $server->on('afterUnbind', [$this, 'afterUnbind']); } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using Sabre\DAV\Server::getPlugin * * @return string */ function getPluginName() { return 'locks'; } /** * This method is called after most properties have been found * it allows us to add in any Lock-related properties * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFind(DAV\PropFind $propFind, DAV\INode $node) { $propFind->handle('{DAV:}supportedlock', function() { return new DAV\Property\SupportedLock(!!$this->locksBackend); }); $propFind->handle('{DAV:}lockdiscovery', function() use ($propFind) { return new DAV\Property\LockDiscovery( $this->getLocks( $propFind->getPath() ) ); }); } /** * 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 */ function getHTTPMethods($uri) { if ($this->locksBackend) return ['LOCK','UNLOCK']; return []; } /** * Returns a list of features for the HTTP OPTIONS Dav: header. * * In this case this is only the number 2. The 2 in the Dav: header * indicates the server supports locks. * * @return array */ function getFeatures() { return [2]; } /** * Returns all lock information on a particular uri * * This function should return an array with Sabre\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array. * * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object * for any possible locks and return those as well. * * @param string $uri * @param bool $returnChildLocks * @return array */ function getLocks($uri, $returnChildLocks = false) { $lockList = []; if ($this->locksBackend) $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks)); return $lockList; } /** * Locks an uri * * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type * of lock (shared or exclusive) and the owner of the lock * * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock * * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3 * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpLock(RequestInterface $request, ResponseInterface $response) { $uri = $request->getPath(); $existingLocks = $this->getLocks($uri); if ($body = $request->getBodyAsString()) { // This is a new lock request $existingLock = null; // Checking if there's already non-shared locks on the uri. foreach($existingLocks as $existingLock) { if ($existingLock->scope === LockInfo::EXCLUSIVE) { throw new DAV\Exception\ConflictingLock($existingLock); } } $lockInfo = $this->parseLockRequest($body); $lockInfo->depth = $this->server->getHTTPDepth(); $lockInfo->uri = $uri; if($existingLock && $lockInfo->scope != LockInfo::SHARED) throw new DAV\Exception\ConflictingLock($existingLock); } else { // Gonna check if this was a lock refresh. $existingLocks = $this->getLocks($uri); $conditions = $this->server->getIfConditions($request); $found = null; foreach($existingLocks as $existingLock) { foreach($conditions as $condition) { foreach($condition['tokens'] as $token) { if ($token['token'] === 'opaquelocktoken:' . $existingLock->token) { $found = $existingLock; break 3; } } } } // If none were found, this request is in error. if (is_null($found)) { if ($existingLocks) { throw new DAV\Exception\Locked(reset($existingLocks)); } else { throw new DAV\Exception\BadRequest('An xml body is required for lock requests'); } } // This must have been a lock refresh $lockInfo = $found; // The resource could have been locked through another uri. if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri; } if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout; $newFile = false; // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first try { $this->server->tree->getNodeForPath($uri); // We need to call the beforeWriteContent event for RFC3744 // Edit: looks like this is not used, and causing problems now. // // See Issue 222 // $this->server->emit('beforeWriteContent',array($uri)); } catch (DAV\Exception\NotFound $e) { // It didn't, lets create it $this->server->createFile($uri,fopen('php://memory','r')); $newFile = true; } $this->lockNode($uri,$lockInfo); $response->setHeader('Content-Type','application/xml; charset=utf-8'); $response->setHeader('Lock-Token','token . '>'); $response->setStatus($newFile?201:200); $response->setBody($this->generateLockResponse($lockInfo)); // Returning false will interupt the event chain and mark this method // as 'handled'. return false; } /** * Unlocks a uri * * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header * The server should return 204 (No content) on success * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function httpUnlock(RequestInterface $request, ResponseInterface $response) { $lockToken = $request->getHeader('Lock-Token'); // If the locktoken header is not supplied, we need to throw a bad request exception if (!$lockToken) throw new DAV\Exception\BadRequest('No lock token was supplied'); $path = $request->getPath(); $locks = $this->getLocks($path); // Windows sometimes forgets to include < and > in the Lock-Token // header if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>'; foreach($locks as $lock) { if ('token . '>' == $lockToken) { $this->unlockNode($path,$lock); $response->setHeader('Content-Length','0'); $response->setStatus(204); // Returning false will break the method chain, and mark the // method as 'handled'. return false; } } // If we got here, it means the locktoken was invalid throw new DAV\Exception\LockTokenMatchesRequestUri(); } /** * This method is called after a node is deleted. * * We use this event to clean up any locks that still exist on the node. * * @param string $path * @return void */ function afterUnbind($path) { $locks = $this->getLocks($path, $includeChildren = true); foreach($locks as $lock) { $this->unlockNode($path, $lock); } } /** * Locks a uri * * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function lockNode($uri,LockInfo $lockInfo) { if (!$this->server->emit('beforeLock', [$uri,$lockInfo])) return; if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo); throw new DAV\Exception\MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.'); } /** * Unlocks a uri * * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function unlockNode($uri, LockInfo $lockInfo) { if (!$this->server->emit('beforeUnlock', [$uri,$lockInfo])) return; if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo); } /** * Returns the contents of the HTTP Timeout header. * * The method formats the header into an integer. * * @return int */ function getTimeoutHeader() { $header = $this->server->httpRequest->getHeader('Timeout'); if ($header) { if (stripos($header,'second-')===0) $header = (int)(substr($header,7)); else if (stripos($header,'infinite')===0) $header = LockInfo::TIMEOUT_INFINITE; else throw new DAV\Exception\BadRequest('Invalid HTTP timeout header'); } else { $header = 0; } return $header; } /** * Generates the response for successful LOCK requests * * @param LockInfo $lockInfo * @return string */ protected function generateLockResponse(LockInfo $lockInfo) { $dom = new \DOMDocument('1.0','utf-8'); $dom->formatOutput = true; $prop = $dom->createElementNS('DAV:','d:prop'); $dom->appendChild($prop); $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery'); $prop->appendChild($lockDiscovery); $lockObj = new DAV\Property\LockDiscovery([$lockInfo]); $lockObj->serialize($this->server,$lockDiscovery); return $dom->saveXML(); } /** * The validateTokens event is triggered before every request. * * It's a moment where this plugin can check all the supplied lock tokens * in the If: header, and check if they are valid. * * In addition, it will also ensure that it checks any missing lokens that * must be present in the request, and reject requests without the proper * tokens. * * @param mixed $conditions * @return void */ function validateTokens( RequestInterface $request, &$conditions ) { // First we need to gather a list of locks that must be satisfied. $mustLocks = []; $method = $request->getMethod(); // Methods not in that list are operations that doesn't alter any // resources, and we don't need to check the lock-states for. switch($method) { case 'DELETE' : $mustLocks = array_merge($mustLocks, $this->getLocks( $request->getPath(), true )); break; case 'MKCOL' : case 'MKCALENDAR' : case 'PROPPATCH' : case 'PUT' : case 'PATCH' : $mustLocks = array_merge($mustLocks, $this->getLocks( $request->getPath(), false )); break; case 'MOVE' : $mustLocks = array_merge($mustLocks, $this->getLocks( $request->getPath(), true )); $mustLocks = array_merge($mustLocks, $this->getLocks( $this->server->calculateUri($request->getHeader('Destination')), false )); break; case 'COPY' : $mustLocks = array_merge($mustLocks, $this->getLocks( $this->server->calculateUri($request->getHeader('Destination')), false )); break; case 'LOCK' : //Temporary measure.. figure out later why this is needed // Here we basically ignore all incoming tokens... foreach($conditions as $ii=>$condition) { foreach($condition['tokens'] as $jj=>$token) { $conditions[$ii]['tokens'][$jj]['validToken'] = true; } } return; } // It's possible that there's identical locks, because of shared // parents. We're removing the duplicates here. $tmp = []; foreach($mustLocks as $lock) $tmp[$lock->token] = $lock; $mustLocks = array_values($tmp); foreach($conditions as $kk=>$condition) { foreach($condition['tokens'] as $ii=>$token) { // Lock tokens always start with opaquelocktoken: if (substr($token['token'], 0, 16) !== 'opaquelocktoken:') { continue; } $checkToken = substr($token['token'],16); // Looping through our list with locks. foreach($mustLocks as $jj => $mustLock) { if ($mustLock->token == $checkToken) { // We have a match! // Removing this one from mustlocks unset($mustLocks[$jj]); // Marking the condition as valid. $conditions[$kk]['tokens'][$ii]['validToken'] = true; // Advancing to the next token continue 2; } // If we got here, it means that there was a // lock-token, but it was not in 'mustLocks'. // // This is an edge-case, as it could mean that token // was specified with a url that was not 'required' to // check. So we're doing one extra lookup to make sure // we really don't know this token. // // This also gets triggered when the user specified a // lock-token that was expired. $oddLocks = $this->getLocks($condition['uri']); foreach($oddLocks as $oddLock) { if ($oddLock->token === $checkToken) { // We have a hit! $conditions[$kk]['tokens'][$ii]['validToken'] = true; continue 2; } } // If we get all the way here, the lock-token was // really unknown. } } } // If there's any locks left in the 'mustLocks' array, it means that // the resource was locked and we must block it. if ($mustLocks) { throw new DAV\Exception\Locked(reset($mustLocks)); } } /** * Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object * * @param string $body * @return LockInfo */ protected function parseLockRequest($body) { // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or // 5.4.13. $previous = libxml_disable_entity_loader(true); $xml = simplexml_load_string( DAV\XMLUtil::convertDAVNamespace($body), null, LIBXML_NOWARNING); libxml_disable_entity_loader($previous); $xml->registerXPathNamespace('d','urn:DAV'); $lockInfo = new LockInfo(); $children = $xml->children("urn:DAV"); $lockInfo->owner = (string)$children->owner; $lockInfo->token = DAV\UUIDUtil::getUUID(); $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0 ? LockInfo::EXCLUSIVE : LockInfo::SHARED; return $lockInfo; } } sabre-dav-2.1.10/lib/DAV/Mount/000077500000000000000000000000001267035675400157405ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAV/Mount/Plugin.php000066400000000000000000000042231267035675400177100ustar00rootroot00000000000000server = $server; $this->server->on('method:GET', [$this,'httpGet'], 90); } /** * 'beforeMethod' event handles. This event handles intercepts GET requests ending * with ?mount * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpGet(RequestInterface $request, ResponseInterface $response) { $queryParams = $request->getQueryParameters(); if (!array_key_exists('mount', $queryParams)) return; $currentUri = $request->getAbsoluteUrl(); // Stripping off everything after the ? list($currentUri) = explode('?',$currentUri); $this->davMount($response, $currentUri); // Returning false to break the event chain return false; } /** * Generates the davmount response * * @param string $uri absolute uri * @return void */ function davMount(ResponseInterface $response, $uri) { $response->setStatus(200); $response->setHeader('Content-Type','application/davmount+xml'); ob_start(); echo '', "\n"; echo "\n"; echo " ", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "\n"; echo ""; $response->setBody(ob_get_clean()); } } sabre-dav-2.1.10/lib/DAV/Node.php000066400000000000000000000020171267035675400162340ustar00rootroot00000000000000addPlugin($patchPlugin); * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/) * @license http://sabre.io/license/ Modified BSD License */ class Plugin extends DAV\ServerPlugin { const RANGE_APPEND = 1; const RANGE_START = 2; const RANGE_END = 3; /** * Reference to server * * @var Sabre\DAV\Server */ protected $server; /** * Initializes the plugin * * This method is automatically called by the Server class after addPlugin. * * @param DAV\Server $server * @return void */ function initialize(DAV\Server $server) { $this->server = $server; $server->on('method:PATCH', [$this,'httpPatch']); } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using DAV\Server::getPlugin * * @return string */ function getPluginName() { return 'partialupdate'; } /** * 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. * * We claim to support PATCH method (partirl update) if and only if * - the node exist * - the node implements our partial update interface * * @param string $uri * @return array */ function getHTTPMethods($uri) { $tree = $this->server->tree; if ($tree->nodeExists($uri)) { $node = $tree->getNodeForPath($uri); if ($node instanceof IFile || $node instanceof IPatchSupport) { return ['PATCH']; } } return []; } /** * Returns a list of features for the HTTP OPTIONS Dav: header. * * @return array */ function getFeatures() { return ['sabredav-partialupdate']; } /** * Patch an uri * * The WebDAV patch request can be used to modify only a part of an * existing resource. If the resource does not exist yet and the first * offset is not 0, the request fails * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function httpPatch(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Get the node. Will throw a 404 if not found $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof IFile && !$node instanceof IPatchSupport) { throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); } $range = $this->getHTTPUpdateRange($request); if (!$range) { throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers'); } $contentType = strtolower( $request->getHeader('Content-Type') ); if ($contentType != 'application/x-sabredav-partialupdate') { throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); } $len = $this->server->httpRequest->getHeader('Content-Length'); if (!$len) throw new DAV\Exception\LengthRequired('A Content-Length header is required'); switch($range[0]) { case self::RANGE_START : // Calculate the end-range if it doesn't exist. if (!$range[2]) { $range[2] = $range[1] + $len - 1; } else { if ($range[2] < $range[1]) { throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[2] . ') is lower than the start offset (' . $range[1] . ')'); } if($range[2] - $range[1] + 1 != $len) { throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[1] . ') and end (' . $range[2] . ') offsets'); } } break; } if (!$this->server->emit('beforeWriteContent', [$path, $node, null])) return; $body = $this->server->httpRequest->getBody(); if ($node instanceof IPatchSupport) { $etag = $node->patch($body, $range[0], isset($range[1])?$range[1]:null); } else { // The old interface switch($range[0]) { case self::RANGE_APPEND : throw new DAV\Exception\NotImplemented('This node does not support the append syntax. Please upgrade it to IPatchSupport'); case self::RANGE_START : $etag = $node->putRange($body, $range[1]); break; case self::RANGE_END : throw new DAV\Exception\NotImplemented('This node does not support the end-range syntax. Please upgrade it to IPatchSupport'); break; } } $this->server->emit('afterWriteContent', [$path, $node]); $response->setHeader('Content-Length','0'); if ($etag) $response->setHeader('ETag',$etag); $response->setStatus(204); // Breaks the event chain return false; } /** * Returns the HTTP custom range update header * * This method returns null if there is no well-formed HTTP range request * header. It returns array(1) if it was an append request, array(2, * $start, $end) if it's a start and end range, lastly it's array(3, * $endoffset) if the offset was negative, and should be calculated from * the end of the file. * * Examples: * * null - invalid * [1] - append * [2,10,15] - update bytes 10, 11, 12, 13, 14, 15 * [2,10,null] - update bytes 10 until the end of the patch body * [3,-5] - update from 5 bytes from the end of the file. * * @param RequestInterface $request * @return array|null */ function getHTTPUpdateRange(RequestInterface $request) { $range = $request->getHeader('X-Update-Range'); if (is_null($range)) return null; // Matching "Range: bytes=1234-5678: both numbers are optional if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i',$range,$matches)) return null; if ($matches[1]==='append') { return [self::RANGE_APPEND]; } elseif (strlen($matches[2])>0) { return [self::RANGE_START, $matches[2], $matches[3]?:null]; } elseif ($matches[4]) { return [self::RANGE_END, $matches[4]]; } else { return null; } } } sabre-dav-2.1.10/lib/DAV/PropFind.php000066400000000000000000000177601267035675400171030ustar00rootroot00000000000000path = $path; $this->properties = $properties; $this->depth = $depth; $this->requestType = $requestType; if($requestType === self::ALLPROPS) { $this->properties = [ '{DAV:}getlastmodified', '{DAV:}getcontentlength', '{DAV:}resourcetype', '{DAV:}quota-used-bytes', '{DAV:}quota-available-bytes', '{DAV:}getetag', '{DAV:}getcontenttype', ]; } foreach($this->properties as $propertyName) { // Seeding properties with 404's. $this->result[$propertyName] = [404, null]; } $this->itemsLeft = count($this->result); } /** * Handles a specific property. * * This method checks wether the specified property was requested in this * PROPFIND request, and if so, it will call the callback and use the * return value for it's value. * * Example: * * $propFind->handle('{DAV:}displayname', function() { * return 'hello'; * }); * * Note that handle will only work the first time. If null is returned, the * value is ignored. * * It's also possible to not pass a callback, but immediately pass a value * * @param string $propertyName * @param mixed $valueOrCallBack * @return void */ function handle($propertyName, $valueOrCallBack) { if ($this->itemsLeft && isset($this->result[$propertyName]) && $this->result[$propertyName][0] === 404) { if (is_callable($valueOrCallBack)) { $value = $valueOrCallBack(); } else { $value = $valueOrCallBack; } if (!is_null($value)) { $this->itemsLeft--; $this->result[$propertyName] = [200, $value]; } } } /** * Sets the value of the property * * If status is not supplied, the status will default to 200 for non-null * properties, and 404 for null properties. * * @param string $propertyName * @param mixed $value * @param int $status * @return void */ function set($propertyName, $value, $status = null) { if (is_null($status)) { $status = is_null($value) ? 404 : 200; } // If this is an ALLPROPS request and the property is // unknown, add it to the result; else ignore it: if (!isset($this->result[$propertyName])) { if ($this->requestType === self::ALLPROPS) { $this->result[$propertyName] = [$status, $value]; } return; } if ($status!==404 && $this->result[$propertyName][0]===404) { $this->itemsLeft--; } elseif ($status === 404 && $this->result[$propertyName][0] !== 404) { $this->itemsLeft++; } $this->result[$propertyName] = [$status, $value]; } /** * Returns the current value for a property. * * @param string $propertyName * @return mixed */ function get($propertyName) { return isset($this->result[$propertyName])?$this->result[$propertyName][1]:null; } /** * Returns the current status code for a property name. * * If the property does not appear in the list of requested properties, * null will be returned. * * @param string $propertyName * @return int|null */ function getStatus($propertyName) { return isset($this->result[$propertyName])?$this->result[$propertyName][0]:null; } /** * Updates the path for this PROPFIND. * * @param string $path * @return void */ function setPath($path) { $this->path = $path; } /** * Returns the path this PROPFIND request is for. * * @return string */ function getPath() { return $this->path; } /** * Returns the depth of this propfind request. * * @return int */ function getDepth() { return $this->depth; } /** * Updates the depth of this propfind request. * * @param int $depth * @return void */ function setDepth($depth) { $this->depth = $depth; } /** * Returns all propertynames that have a 404 status, and thus don't have a * value yet. * * @return array */ function get404Properties() { if ($this->itemsLeft === 0) { return []; } $result = []; foreach($this->result as $propertyName=>$stuff) { if ($stuff[0]===404) { $result[] = $propertyName; } } return $result; } /** * Returns the full list of requested properties. * * This returns just their names, not a status or value. * * @return array */ function getRequestedProperties() { return $this->properties; } /** * Returns a result array that's often used in multistatus responses. * * The array uses status codes as keys, and property names and value pairs * as the value of the top array.. such as : * * [ * 200 => [ '{DAV:}displayname' => 'foo' ], * ] * * @return array */ function getResultForMultiStatus() { $r = [ 200 => [], 404 => [], ]; foreach($this->result as $propertyName=>$info) { if (!isset($r[$info[0]])) { $r[$info[0]] = [$propertyName => $info[1]]; } else { $r[$info[0]][$propertyName] = $info[1]; } } // Removing the 404's for multi-status requests. if ($this->requestType === self::ALLPROPS) unset($r[404]); return $r; } /** * The path that we're fetching properties for. * * @var string */ protected $path; /** * The Depth of the request. * * 0 means only the current item. 1 means the current item + its children. * It can also be DEPTH_INFINITY if this is enabled in the server. * * @var int */ protected $depth = 0; /** * The type of request. See the TYPE constants */ protected $requestType; /** * A list of requested properties * * @var array */ protected $properties = []; /** * The result of the operation. * * The keys in this array are property names. * The values are an array with two elements: the http status code and then * optionally a value. * * Example: * * [ * "{DAV:}owner" : [404], * "{DAV:}displayname" : [200, "Admin"] * ] * * @var array */ protected $result = []; /** * This is used as an internal counter for the number of properties that do * not yet have a value. * * @var int */ protected $itemsLeft; } sabre-dav-2.1.10/lib/DAV/PropPatch.php000066400000000000000000000215561267035675400172600ustar00rootroot00000000000000mutations = $mutations; } /** * Call this function if you wish to handle updating certain properties. * For instance, your class may be responsible for handling updates for the * {DAV:}displayname property. * * In that case, call this method with the first argument * "{DAV:}displayname" and a second argument that's a method that does the * actual updating. * * It's possible to specify more than one property. * * @param string|string[] $properties * @param callable $callback * @return void */ function handle($properties, callable $callback) { $usedProperties = []; foreach((array)$properties as $propertyName) { if (array_key_exists($propertyName, $this->mutations) && !isset($this->result[$propertyName])) { $usedProperties[] = $propertyName; // HTTP Accepted $this->result[$propertyName] = 202; } } // Only registering if there's any unhandled properties. if (!$usedProperties) { return; } $this->propertyUpdateCallbacks[] = [ // If the original argument to this method was a string, we need // to also make sure that it stays that way, so the commit function // knows how to format the arguments to the callback. is_string($properties)?$properties:$usedProperties, $callback ]; } /** * Call this function if you wish to handle _all_ properties that haven't * been handled by anything else yet. Note that you effectively claim with * this that you promise to process _all_ properties that are coming in. * * @param callable $callback * @return void */ function handleRemaining(callable $callback) { $properties = $this->getRemainingMutations(); if (!$properties) { // Nothing to do, don't register callback return; } foreach($properties as $propertyName) { // HTTP Accepted $this->result[$propertyName] = 202; $this->propertyUpdateCallbacks[] = [ $properties, $callback ]; } } /** * Sets the result code for one or more properties. * * @param string|string[] $properties * @param int $resultCode * @return void */ function setResultCode($properties, $resultCode) { foreach((array)$properties as $propertyName) { $this->result[$propertyName] = $resultCode; } if ($resultCode>=400) { $this->failed = true; } } /** * Sets the result code for all properties that did not have a result yet. * * @param int $resultCode * @return void */ function setRemainingResultCode($resultCode) { $this->setResultCode( $this->getRemainingMutations(), $resultCode ); } /** * Returns the list of properties that don't have a result code yet. * * @return array */ function getRemainingMutations() { $remaining = []; foreach($this->mutations as $propertyName => $propValue) { if (!isset($this->result[$propertyName])) { $remaining[] = $propertyName; } } return $remaining; } /** * Performs the actual update, and calls all callbacks. * * This method returns true or false depending on if the operation was * successful. * * @return bool */ function commit() { // First we validate if every property has a handler foreach($this->mutations as $propertyName => $value) { if (!isset($this->result[$propertyName])) { $this->failed = true; $this->result[$propertyName] = 403; } } foreach($this->propertyUpdateCallbacks as $callbackInfo) { if ($this->failed) { break; } if (is_string($callbackInfo[0])) { $this->doCallbackSingleProp($callbackInfo[0], $callbackInfo[1]); } else { $this->doCallbackMultiProp($callbackInfo[0], $callbackInfo[1]); } } /** * If anywhere in this operation updating a property failed, we must * update all other properties accordingly. */ if ($this->failed) { foreach($this->result as $propertyName=>$status) { if ($status === 202) { // Failed dependency $this->result[$propertyName] = 424; } } } return !$this->failed; } /** * Executes a property callback with the single-property syntax. * * @param string $propertyName * @param callable $callback * @return void */ private function doCallBackSingleProp($propertyName, callable $callback) { $result = $callback($this->mutations[$propertyName]); if (is_bool($result)) { if ($result) { if (is_null($this->mutations[$propertyName])) { // Delete $result = 204; } else { // Update $result = 200; } } else { // Fail $result = 403; } } if (!is_int($result)) { throw new UnexpectedValueException('A callback sent to handle() did not return an int or a bool'); } $this->result[$propertyName] = $result; if ($result>=400) { $this->failed = true; } } /** * Executes a property callback with the multi-property syntax. * * @param array $propertyList * @param callable $callback * @return void */ private function doCallBackMultiProp(array $propertyList, callable $callback) { $argument = []; foreach($propertyList as $propertyName) { $argument[$propertyName] = $this->mutations[$propertyName]; } $result = $callback($argument); if (is_array($result)) { foreach($propertyList as $propertyName) { if (!isset($result[$propertyName])) { $resultCode = 500; } else { $resultCode = $result[$propertyName]; } if ($resultCode >= 400) { $this->failed = true; } $this->result[$propertyName] = $resultCode; } } elseif ($result === true) { // Success foreach($argument as $propertyName=>$propertyValue) { $this->result[$propertyName] = is_null($propertyValue)?204:200; } } elseif ($result === false) { // Fail :( $this->failed = true; foreach($propertyList as $propertyName) { $this->result[$propertyName] = 403; } } else { throw new UnexpectedValueException('A callback sent to handle() did not return an array or a bool'); } } /** * Returns the result of the operation. * * @return array */ function getResult() { return $this->result; } /** * Returns the full list of mutations * * @return array */ function getMutations() { return $this->mutations; } } sabre-dav-2.1.10/lib/DAV/Property.php000066400000000000000000000013461267035675400171770ustar00rootroot00000000000000time = $time; } elseif (is_int($time) || ctype_digit($time)) { $this->time = new \DateTime('@' . $time); } else { $this->time = new \DateTime($time); } // Setting timezone to UTC $this->time->setTimezone(new \DateTimeZone('UTC')); } /** * serialize * * @param DAV\Server $server * @param \DOMElement $prop * @return void */ function serialize(DAV\Server $server, \DOMElement $prop) { //$prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/'); //$prop->setAttribute('b:dt','dateTime.rfc1123'); $prop->nodeValue = HTTP\Util::toHTTPDate($this->time); } /** * getTime * * @return \DateTime */ function getTime() { return $this->time; } } sabre-dav-2.1.10/lib/DAV/Property/Href.php000066400000000000000000000044241267035675400200630ustar00rootroot00000000000000href = $href; $this->autoPrefix = $autoPrefix; } /** * Returns the uri * * @return string */ function getHref() { return $this->href; } /** * Serializes this property. * * It will additionally prepend the href property with the server's base uri. * * @param DAV\Server $server * @param \DOMElement $dom * @return void */ function serialize(DAV\Server $server, \DOMElement $dom) { $prefix = $server->xmlNamespaces['DAV:']; $elem = $dom->ownerDocument->createElement($prefix . ':href'); if ($this->autoPrefix) { $value = $server->getBaseUri() . DAV\URLUtil::encodePath($this->href); } else { $value = $this->href; } $elem->appendChild($dom->ownerDocument->createTextNode($value)); $dom->appendChild($elem); } /** * Unserializes this property from a DOM Element * * This method returns an instance of this class. * It will only decode {DAV:}href values. For non-compatible elements null will be returned. * * @param \DOMElement $dom * @param array $propertyMap * @return DAV\Property\Href */ static function unserialize(\DOMElement $dom, array $propertyMap) { if ($dom->firstChild && DAV\XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') { return new self($dom->firstChild->textContent,false); } } } sabre-dav-2.1.10/lib/DAV/Property/HrefList.php000066400000000000000000000044621267035675400207210ustar00rootroot00000000000000hrefs = $hrefs; $this->autoPrefix = $autoPrefix; } /** * Returns the uris * * @return array */ function getHrefs() { return $this->hrefs; } /** * Serializes this property. * * It will additionally prepend the href property with the server's base uri. * * @param DAV\Server $server * @param \DOMElement $dom * @return void */ function serialize(DAV\Server $server,\DOMElement $dom) { $prefix = $server->xmlNamespaces['DAV:']; foreach($this->hrefs as $href) { $elem = $dom->ownerDocument->createElement($prefix . ':href'); if ($this->autoPrefix) { $value = $server->getBaseUri() . DAV\URLUtil::encodePath($href); } else { $value = $href; } $elem->appendChild($dom->ownerDocument->createTextNode($value)); $dom->appendChild($elem); } } /** * Unserializes this property from a DOM Element * * This method returns an instance of this class. * It will only decode {DAV:}href values. * * @param \DOMElement $dom * @param array $propertyMap * @return DAV\Property\HrefList */ static function unserialize(\DOMElement $dom, array $propertyMap) { $hrefs = []; foreach($dom->childNodes as $child) { if (DAV\XMLUtil::toClarkNotation($child)==='{DAV:}href') { $hrefs[] = $child->textContent; } } return new self($hrefs, false); } } sabre-dav-2.1.10/lib/DAV/Property/IHref.php000066400000000000000000000010031267035675400201620ustar00rootroot00000000000000locks = $locks; } /** * serialize * * @param DAV\Server $server * @param \DOMElement $prop * @return void */ function serialize(DAV\Server $server, \DOMElement $prop) { $doc = $prop->ownerDocument; foreach($this->locks as $lock) { $activeLock = $doc->createElementNS('DAV:','d:activelock'); $prop->appendChild($activeLock); $lockScope = $doc->createElementNS('DAV:','d:lockscope'); $activeLock->appendChild($lockScope); $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==DAV\Locks\LockInfo::EXCLUSIVE?'exclusive':'shared'))); $lockType = $doc->createElementNS('DAV:','d:locktype'); $activeLock->appendChild($lockType); $lockType->appendChild($doc->createElementNS('DAV:','d:write')); /* {DAV:}lockroot */ if (!self::$hideLockRoot) { $lockRoot = $doc->createElementNS('DAV:','d:lockroot'); $activeLock->appendChild($lockRoot); $href = $doc->createElementNS('DAV:','d:href'); $href->appendChild($doc->createTextNode($server->getBaseUri() . $lock->uri)); $lockRoot->appendChild($href); } $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == DAV\Server::DEPTH_INFINITY?'infinity':$lock->depth))); $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout)); $lockToken = $doc->createElementNS('DAV:','d:locktoken'); $activeLock->appendChild($lockToken); $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token)); $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner)); } } } sabre-dav-2.1.10/lib/DAV/Property/ResourceType.php000066400000000000000000000052511267035675400216270ustar00rootroot00000000000000resourceType = $resourceType; else $this->resourceType = [$resourceType]; } /** * serialize * * @param DAV\Server $server * @param \DOMElement $prop * @return void */ function serialize(DAV\Server $server, \DOMElement $prop) { $propName = null; $rt = $this->resourceType; foreach($rt as $resourceType) { if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { if (isset($server->xmlNamespaces[$propName[1]])) { $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2])); } else { $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2])); } } } } /** * Returns the values in clark-notation * * For example ['{DAV:}collection'] * * @return array */ function getValue() { return $this->resourceType; } /** * Checks if the principal contains a certain value * * @param string $type * @return bool */ function is($type) { return in_array($type, $this->resourceType); } /** * Adds a resourcetype value to this property * * @param string $type * @return void */ function add($type) { $this->resourceType[] = $type; $this->resourceType = array_unique($this->resourceType); } /** * Unserializes a DOM element into a ResourceType property. * * @param \DOMElement $dom * @param array $propertyMap * @return DAV\Property\ResourceType */ static function unserialize(\DOMElement $dom, array $propertyMap) { $value = []; foreach($dom->childNodes as $child) { $value[] = DAV\XMLUtil::toClarkNotation($child); } return new self($value); } } sabre-dav-2.1.10/lib/DAV/Property/Response.php000066400000000000000000000145621267035675400210010ustar00rootroot00000000000000href = $href; $this->responseProperties = $responseProperties; $this->httpStatus = $httpStatus; } /** * Returns the url * * @return string */ function getHref() { return $this->href; } /** * Returns the httpStatus value * * @return string */ function getHttpStatus() { return $this->httpStatus; } /** * Returns the property list * * @return array */ function getResponseProperties() { return $this->responseProperties; } /** * serialize * * @param DAV\Server $server * @param \DOMElement $dom * @return void */ function serialize(DAV\Server $server, \DOMElement $dom) { $document = $dom->ownerDocument; $properties = $this->responseProperties; $xresponse = $document->createElement('d:response'); $dom->appendChild($xresponse); $uri = URLUtil::encodePath($this->href); if ($uri==='/') $uri = ''; // Adding the baseurl to the beginning of the url $uri = $server->getBaseUri() . $uri; $xresponse->appendChild($document->createElement('d:href',$uri)); if ($this->httpStatus) { $statusString = "HTTP/1.1 " . $this->httpStatus . " " . HTTP\Response::$statusCodes[$this->httpStatus]; $xresponse->appendChild($document->createElement('d:status', $statusString)); } // The properties variable is an array containing properties, grouped by // HTTP status foreach($properties as $httpStatus=>$propertyGroup) { // The 'href' is also in this array, and it's special cased. // We will ignore it if ($httpStatus=='href') continue; // If there are no properties in this group, we can also just carry on if (!count($propertyGroup)) continue; $xpropstat = $document->createElement('d:propstat'); $xresponse->appendChild($xpropstat); $xprop = $document->createElement('d:prop'); $xpropstat->appendChild($xprop); $nsList = $server->xmlNamespaces; foreach($propertyGroup as $propertyName=>$propertyValue) { $propName = null; preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); // special case for empty namespaces if ($propName[1]=='') { $currentProperty = $document->createElement($propName[2]); $xprop->appendChild($currentProperty); $currentProperty->setAttribute('xmlns',''); } else { if (!isset($nsList[$propName[1]])) { $nsList[$propName[1]] = 'x' . count($nsList); } // If the namespace was defined in the top-level xml namespaces, it means // there was already a namespace declaration, and we don't have to worry about it. if (isset($server->xmlNamespaces[$propName[1]])) { $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]); } else { $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]); } $xprop->appendChild($currentProperty); } if (is_scalar($propertyValue)) { if ($propertyValue!=='') { // we want a self-closing xml element for empty strings. $text = $document->createTextNode($propertyValue); $currentProperty->appendChild($text); } } elseif ($propertyValue instanceof DAV\PropertyInterface) { $propertyValue->serialize($server,$currentProperty); } elseif (!is_null($propertyValue)) { throw new DAV\Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName); } } $xpropstat->appendChild($document->createElement('d:status','HTTP/1.1 ' . $httpStatus . ' ' . HTTP\Response::$statusCodes[$httpStatus])); } } /** * Unserializes the property. * * This static method should return a an instance of this object. * * @param \DOMElement $prop * @param array $propertyMap * @return DAV\IProperty */ static function unserialize(\DOMElement $prop, array $propertyMap) { // Delegating this to the ResponseList property. It does make more // sense there. $result = ResponseList::unserialize($prop, $propertyMap); $result = $result->getResponses(); return $result[0]; } } sabre-dav-2.1.10/lib/DAV/Property/ResponseList.php000066400000000000000000000063331267035675400216320ustar00rootroot00000000000000responses = $responses; } /** * Returns the list of Response properties. * * @return Response[] */ function getResponses() { return $this->responses; } /** * serialize * * @param DAV\Server $server * @param \DOMElement $dom * @return void */ function serialize(DAV\Server $server,\DOMElement $dom) { foreach($this->responses as $response) { $response->serialize($server, $dom); } } /** * Unserializes the property. * * This static method should return a an instance of this object. * * @param \DOMElement $prop * @param array $propertyMap * @return DAV\IProperty */ static function unserialize(\DOMElement $prop, array $propertyMap) { $xpath = new \DOMXPath( $prop->ownerDocument ); $xpath->registerNamespace('d','urn:DAV'); // Finding the 'response' element $xResponses = $xpath->evaluate( 'd:response', $prop ); $result = []; for($jj=0; $jj < $xResponses->length; $jj++) { $xResponse = $xResponses->item($jj); // Parsing 'href' $href = Href::unserialize($xResponse, $propertyMap); $properties = []; // Parsing 'status' in 'd:response' $responseStatus = $xpath->evaluate('string(d:status)', $xResponse); if ($responseStatus) { list(, $responseStatus,) = explode(' ', $responseStatus, 3); } // Parsing 'propstat' $xPropstat = $xpath->query('d:propstat', $xResponse); for($ii=0; $ii < $xPropstat->length; $ii++) { // Parsing 'status' $status = $xpath->evaluate('string(d:status)', $xPropstat->item($ii)); list(,$statusCode,) = explode(' ', $status, 3); $usedPropertyMap = $statusCode == '200' ? $propertyMap : []; // Parsing 'prop' $properties[$statusCode] = DAV\XMLUtil::parseProperties($xPropstat->item($ii), $usedPropertyMap); } $result[] = new Response($href->getHref(), $properties, $responseStatus?$responseStatus:null); } return new self($result); } } sabre-dav-2.1.10/lib/DAV/Property/SupportedLock.php000066400000000000000000000041311267035675400217700ustar00rootroot00000000000000supportsLocks = $supportsLocks; } /** * serialize * * @param DAV\Server $server * @param \DOMElement $prop * @return void */ function serialize(DAV\Server $server,\DOMElement $prop) { $doc = $prop->ownerDocument; if (!$this->supportsLocks) return null; $lockEntry1 = $doc->createElement('d:lockentry'); $lockEntry2 = $doc->createElement('d:lockentry'); $prop->appendChild($lockEntry1); $prop->appendChild($lockEntry2); $lockScope1 = $doc->createElement('d:lockscope'); $lockScope2 = $doc->createElement('d:lockscope'); $lockType1 = $doc->createElement('d:locktype'); $lockType2 = $doc->createElement('d:locktype'); $lockEntry1->appendChild($lockScope1); $lockEntry1->appendChild($lockType1); $lockEntry2->appendChild($lockScope2); $lockEntry2->appendChild($lockType2); $lockScope1->appendChild($doc->createElement('d:exclusive')); $lockScope2->appendChild($doc->createElement('d:shared')); $lockType1->appendChild($doc->createElement('d:write')); $lockType2->appendChild($doc->createElement('d:write')); //$frag->appendXML(''); //$frag->appendXML(''); } } sabre-dav-2.1.10/lib/DAV/Property/SupportedMethodSet.php000066400000000000000000000024571267035675400230050ustar00rootroot00000000000000methods = $method; } /** * Returns the list of supported methods. * * @return string[] */ function getValue() { return $this->methods; } /** * Serializes the node * * @param DAV\Server $server * @param \DOMElement $prop * @return void */ function serialize(DAV\Server $server, \DOMElement $prop) { foreach($this->methods as $method) { $supportedMethod = $prop->ownerDocument->createElement('d:supported-method'); $supportedMethod->setAttribute('name', $method); $prop->appendChild($supportedMethod); } } } sabre-dav-2.1.10/lib/DAV/Property/SupportedReportSet.php000066400000000000000000000055471267035675400230430ustar00rootroot00000000000000addReport($reports); } /** * Adds a report to this property * * The report must be a string in clark-notation. * Multiple reports can be specified as an array. * * @param mixed $report * @return void */ function addReport($report) { if (!is_array($report)) $report = [$report]; foreach($report as $r) { if (!preg_match('/^{([^}]*)}(.*)$/',$r)) throw new DAV\Exception('Reportname must be in clark-notation'); $this->reports[] = $r; } } /** * Returns the list of supported reports * * @return array */ function getValue() { return $this->reports; } /** * Returns true or false if the property contains a specific report. * * @param string $reportName * @return bool */ function has($reportName) { return in_array( $reportName, $this->reports ); } /** * Serializes the node * * @param DAV\Server $server * @param \DOMElement $prop * @return void */ function serialize(DAV\Server $server, \DOMElement $prop) { foreach($this->reports as $reportName) { $supportedReport = $prop->ownerDocument->createElement('d:supported-report'); $prop->appendChild($supportedReport); $report = $prop->ownerDocument->createElement('d:report'); $supportedReport->appendChild($report); preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches); list(, $namespace, $element) = $matches; $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null; if ($prefix) { $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element)); } else { $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element)); } } } } sabre-dav-2.1.10/lib/DAV/PropertyInterface.php000066400000000000000000000017121267035675400210150ustar00rootroot00000000000000pdo = $pdo; } /** * Fetches properties for a path. * * This method received a PropFind object, which contains all the * information about the properties that need to be fetched. * * Ususually you would just want to call 'get404Properties' on this object, * as this will give you the _exact_ list of properties that need to be * fetched, and haven't yet. * * @param string $path * @param PropFind $propFind * @return void */ function propFind($path, PropFind $propFind) { $propertyNames = $propFind->get404Properties(); if (!$propertyNames) { return; } $query = 'SELECT name, value FROM propertystorage WHERE path = ?'; $stmt = $this->pdo->prepare($query); $stmt->execute([$path]); while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $propFind->set($row['name'], $row['value']); } } /** * Updates properties for a path * * This method received a PropPatch object, which contains all the * information about the update. * * Usually you would want to call 'handleRemaining' on this object, to get; * a list of all properties that need to be stored. * * @param string $path * @param PropPatch $propPatch * @return void */ function propPatch($path, PropPatch $propPatch) { $propPatch->handleRemaining(function($properties) use ($path) { $updateStmt = $this->pdo->prepare("REPLACE INTO propertystorage (path, name, value) VALUES (?, ?, ?)"); $deleteStmt = $this->pdo->prepare("DELETE FROM propertystorage WHERE path = ? AND name = ?"); foreach($properties as $name=>$value) { if (!is_null($value)) { $updateStmt->execute([$path, $name, $value]); } else { $deleteStmt->execute([$path, $name]); } } return true; }); } /** * This method is called after a node is deleted. * * This allows a backend to clean up all associated properties. * * The delete method will get called once for the deletion of an entire * tree. * * @param string $path * @return void */ function delete($path) { $stmt = $this->pdo->prepare("DELETE FROM propertystorage WHERE path = ? OR path LIKE ? ESCAPE '='"); $childPath = strtr( $path, [ '=' => '==', '%' => '=%', '_' => '=_' ] ) . '/%'; $stmt->execute([$path, $childPath]); } /** * This method is called after a successful MOVE * * This should be used to migrate all properties from one path to another. * Note that entire collections may be moved, so ensure that all properties * for children are also moved along. * * @param string $source * @param string $destination * @return void */ function move($source, $destination) { // I don't know a way to write this all in a single sql query that's // also compatible across db engines, so we're letting PHP do all the // updates. Much slower, but it should still be pretty fast in most // cases. $select = $this->pdo->prepare('SELECT id, path FROM propertystorage WHERE path = ? OR path LIKE ?'); $select->execute([$source, $source . '/%']); $update = $this->pdo->prepare('UPDATE propertystorage SET path = ? WHERE id = ?'); while($row = $select->fetch(\PDO::FETCH_ASSOC)) { // Sanity check. SQL may select too many records, such as records // with different cases. if ($row['path'] !== $source && strpos($row['path'], $source . '/')!==0) continue; $trailingPart = substr($row['path'], strlen($source)+1); $newPath = $destination; if ($trailingPart) { $newPath.='/' . $trailingPart; } $update->execute([$newPath, $row['id']]); } } } sabre-dav-2.1.10/lib/DAV/PropertyStorage/Plugin.php000066400000000000000000000072741267035675400217700ustar00rootroot00000000000000backend = $backend; } /** * This initializes the plugin. * * This function is called by Sabre\DAV\Server, after * addPlugin is called. * * This method should set up the required event subscriptions. * * @param Server $server * @return void */ function initialize(Server $server) { $server->on('propFind', [$this, 'propFind'], 130); $server->on('propPatch', [$this, 'propPatch'], 300); $server->on('afterMove', [$this, 'afterMove']); $server->on('afterUnbind', [$this, 'afterUnbind']); } /** * Called during PROPFIND operations. * * If there's any requested properties that don't have a value yet, this * plugin will look in the property storage backend to find them. * * @param PropFind $propFind * @param INode $node * @return void */ function propFind(PropFind $propFind, INode $node) { $path = $propFind->getPath(); $pathFilter = $this->pathFilter; if ($pathFilter && !$pathFilter($path)) return; $this->backend->propFind($propFind->getPath(), $propFind); } /** * Called during PROPPATCH operations * * If there's any updated properties that haven't been stored, the * propertystorage backend can handle it. * * @param string $path * @param PropPatch $propPatch * @return void */ function propPatch($path, PropPatch $propPatch) { $pathFilter = $this->pathFilter; if ($pathFilter && !$pathFilter($path)) return; $this->backend->propPatch($path, $propPatch); } /** * Called after a node is deleted. * * This allows the backend to clean up any properties still in the * database. * * @param string $path * @return void */ function afterUnbind($path) { $pathFilter = $this->pathFilter; if ($pathFilter && !$pathFilter($path)) return; $this->backend->delete($path); } /** * Called after a node is moved. * * This allows the backend to move all the associated properties. * * @param string $source * @param string $destination * @return void */ function afterMove($source, $destination) { $pathFilter = $this->pathFilter; if ($pathFilter && !$pathFilter($source)) return; // If the destination is filtered, afterUnbind will handle cleaning up // the properties. if ($pathFilter && !$pathFilter($destination)) return; $this->backend->move($source, $destination); } } sabre-dav-2.1.10/lib/DAV/Server.php000066400000000000000000001554201267035675400166240ustar00rootroot00000000000000 'd', 'http://sabredav.org/ns' => 's', ]; /** * The propertymap can be used to map properties from * requests to property classes. * * @var array */ public $propertyMap = [ '{DAV:}resourcetype' => 'Sabre\\DAV\\Property\\ResourceType', ]; public $protectedProperties = [ // RFC4918 '{DAV:}getcontentlength', '{DAV:}getetag', '{DAV:}getlastmodified', '{DAV:}lockdiscovery', '{DAV:}supportedlock', // RFC4331 '{DAV:}quota-available-bytes', '{DAV:}quota-used-bytes', // RFC3744 '{DAV:}supported-privilege-set', '{DAV:}current-user-privilege-set', '{DAV:}acl', '{DAV:}acl-restrictions', '{DAV:}inherited-acl-set', // RFC3253 '{DAV:}supported-method-set', '{DAV:}supported-report-set', // RFC6578 '{DAV:}sync-token', // calendarserver.org extensions '{http://calendarserver.org/ns/}ctag', // sabredav extensions '{http://sabredav.org/ns}sync-token', ]; /** * This is a flag that allow or not showing file, line and code * of the exception in the returned XML * * @var bool */ public $debugExceptions = false; /** * This property allows you to automatically add the 'resourcetype' value * based on a node's classname or interface. * * The preset ensures that {DAV:}collection is automatically added for nodes * implementing Sabre\DAV\ICollection. * * @var array */ public $resourceTypeMapping = [ 'Sabre\\DAV\\ICollection' => '{DAV:}collection', ]; /** * This property allows the usage of Depth: infinity on PROPFIND requests. * * By default Depth: infinity is treated as Depth: 1. Allowing Depth: * infinity is potentially risky, as it allows a single client to do a full * index of the webdav server, which is an easy DoS attack vector. * * Only turn this on if you know what you're doing. * * @var bool */ public $enablePropfindDepthInfinity = false; /** * If this setting is turned off, SabreDAV's version number will be hidden * from various places. * * Some people feel this is a good security measure. * * @var bool */ static public $exposeVersion = true; /** * Sets up the server * * If a Sabre\DAV\Tree object is passed as an argument, it will * use it as the directory tree. If a Sabre\DAV\INode is passed, it * will create a Sabre\DAV\Tree and use the node as the root. * * If nothing is passed, a Sabre\DAV\SimpleCollection is created in * a Sabre\DAV\Tree. * * If an array is passed, we automatically create a root node, and use * the nodes in the array as top-level children. * * @param Tree|INode|array|null $treeOrNode The tree object */ function __construct($treeOrNode = null) { if ($treeOrNode instanceof Tree) { $this->tree = $treeOrNode; } elseif ($treeOrNode instanceof INode) { $this->tree = new Tree($treeOrNode); } elseif (is_array($treeOrNode)) { // If it's an array, a list of nodes was passed, and we need to // create the root node. foreach($treeOrNode as $node) { if (!($node instanceof INode)) { throw new Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre\\DAV\\INode'); } } $root = new SimpleCollection('root', $treeOrNode); $this->tree = new Tree($root); } elseif (is_null($treeOrNode)) { $root = new SimpleCollection('root'); $this->tree = new Tree($root); } else { throw new Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre\\DAV\\Tree, Sabre\\DAV\\INode, an array or null'); } $this->sapi = new HTTP\Sapi(); $this->httpResponse = new HTTP\Response(); $this->httpRequest = $this->sapi->getRequest(); $this->addPlugin(new CorePlugin()); } /** * Starts the DAV Server * * @return void */ function exec() { try { // If nginx (pre-1.2) is used as a proxy server, and SabreDAV as an // origin, we must make sure we send back HTTP/1.0 if this was // requested. // This is mainly because nginx doesn't support Chunked Transfer // Encoding, and this forces the webserver SabreDAV is running on, // to buffer entire responses to calculate Content-Length. $this->httpResponse->setHTTPVersion($this->httpRequest->getHTTPVersion()); // Setting the base url $this->httpRequest->setBaseUrl($this->getBaseUri()); $this->invokeMethod($this->httpRequest, $this->httpResponse); } catch (\Exception $e) { try { $this->emit('exception', [$e]); } catch (\Exception $ignore) { } $DOM = new \DOMDocument('1.0','utf-8'); $DOM->formatOutput = true; $error = $DOM->createElementNS('DAV:','d:error'); $error->setAttribute('xmlns:s',self::NS_SABREDAV); $DOM->appendChild($error); $h = function($v) { return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8'); }; if (self::$exposeVersion) { $error->appendChild($DOM->createElement('s:sabredav-version',$h(Version::VERSION))); } $error->appendChild($DOM->createElement('s:exception',$h(get_class($e)))); $error->appendChild($DOM->createElement('s:message',$h($e->getMessage()))); if ($this->debugExceptions) { $error->appendChild($DOM->createElement('s:file',$h($e->getFile()))); $error->appendChild($DOM->createElement('s:line',$h($e->getLine()))); $error->appendChild($DOM->createElement('s:code',$h($e->getCode()))); $error->appendChild($DOM->createElement('s:stacktrace',$h($e->getTraceAsString()))); } if ($this->debugExceptions) { $previous = $e; while ($previous = $previous->getPrevious()) { $xPrevious = $DOM->createElement('s:previous-exception'); $xPrevious->appendChild($DOM->createElement('s:exception',$h(get_class($previous)))); $xPrevious->appendChild($DOM->createElement('s:message',$h($previous->getMessage()))); $xPrevious->appendChild($DOM->createElement('s:file',$h($previous->getFile()))); $xPrevious->appendChild($DOM->createElement('s:line',$h($previous->getLine()))); $xPrevious->appendChild($DOM->createElement('s:code',$h($previous->getCode()))); $xPrevious->appendChild($DOM->createElement('s:stacktrace',$h($previous->getTraceAsString()))); $error->appendChild($xPrevious); } } if($e instanceof Exception) { $httpCode = $e->getHTTPCode(); $e->serialize($this,$error); $headers = $e->getHTTPHeaders($this); } else { $httpCode = 500; $headers = []; } $headers['Content-Type'] = 'application/xml; charset=utf-8'; $this->httpResponse->setStatus($httpCode); $this->httpResponse->setHeaders($headers); $this->httpResponse->setBody($DOM->saveXML()); $this->sapi->sendResponse($this->httpResponse); } } /** * Sets the base server uri * * @param string $uri * @return void */ function setBaseUri($uri) { // If the baseUri does not end with a slash, we must add it if ($uri[strlen($uri)-1]!=='/') $uri.='/'; $this->baseUri = $uri; } /** * Returns the base responding uri * * @return string */ function getBaseUri() { if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri(); return $this->baseUri; } /** * This method attempts to detect the base uri. * Only the PATH_INFO variable is considered. * * If this variable is not set, the root (/) is assumed. * * @return string */ function guessBaseUri() { $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO'); $uri = $this->httpRequest->getRawServerValue('REQUEST_URI'); // If PATH_INFO is found, we can assume it's accurate. if (!empty($pathInfo)) { // We need to make sure we ignore the QUERY_STRING part if ($pos = strpos($uri,'?')) $uri = substr($uri,0,$pos); // PATH_INFO is only set for urls, such as: /example.php/path // in that case PATH_INFO contains '/path'. // Note that REQUEST_URI is percent encoded, while PATH_INFO is // not, Therefore they are only comparable if we first decode // REQUEST_INFO as well. $decodedUri = URLUtil::decodePath($uri); // A simple sanity check: if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) { $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo)); return rtrim($baseUri,'/') . '/'; } throw new Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); } // The last fallback is that we're just going to assume the server root. return '/'; } /** * Adds a plugin to the server * * For more information, console the documentation of Sabre\DAV\ServerPlugin * * @param ServerPlugin $plugin * @return void */ function addPlugin(ServerPlugin $plugin) { $this->plugins[$plugin->getPluginName()] = $plugin; $plugin->initialize($this); } /** * Returns an initialized plugin by it's name. * * This function returns null if the plugin was not found. * * @param string $name * @return ServerPlugin */ function getPlugin($name) { if (isset($this->plugins[$name])) return $this->plugins[$name]; // This is a fallback and deprecated. foreach($this->plugins as $plugin) { if (get_class($plugin)===$name) return $plugin; } return null; } /** * Returns all plugins * * @return array */ function getPlugins() { return $this->plugins; } /** * Handles a http request, and execute a method based on its name * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function invokeMethod(RequestInterface $request, ResponseInterface $response) { $method = $request->getMethod(); if (!$this->emit('beforeMethod:' . $method, [$request, $response])) return; if (!$this->emit('beforeMethod', [$request, $response])) return; if (Server::$exposeVersion) { $response->setHeader('X-Sabre-Version', Version::VERSION); } $this->transactionType = strtolower($method); if (!$this->checkPreconditions($request, $response)) { $this->sapi->sendResponse($response); return; } if ($this->emit('method:' . $method, [$request, $response])) { if ($this->emit('method', [$request, $response])) { // Unsupported method throw new Exception\NotImplemented('There was no handler found for this "' . $method . '" method'); } } if (!$this->emit('afterMethod:' . $method, [$request, $response])) return; if (!$this->emit('afterMethod', [$request, $response])) return; $this->sapi->sendResponse($response); $this->emit('afterResponse', [$request, $response]); } // {{{ HTTP/WebDAV protocol helpers /** * Returns an array with all the supported HTTP methods for a specific uri. * * @param string $path * @return array */ function getAllowedMethods($path) { $methods = [ 'OPTIONS', 'GET', 'HEAD', 'DELETE', 'PROPFIND', 'PUT', 'PROPPATCH', 'COPY', 'MOVE', 'REPORT' ]; // The MKCOL is only allowed on an unmapped uri try { $this->tree->getNodeForPath($path); } catch (Exception\NotFound $e) { $methods[] = 'MKCOL'; } // We're also checking if any of the plugins register any new methods foreach($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($path)); array_unique($methods); return $methods; } /** * Gets the uri for the request, keeping the base uri into consideration * * @return string */ function getRequestUri() { return $this->calculateUri($this->httpRequest->getUrl()); } /** * Calculates the uri for a request, making sure that the base uri is stripped out * * @param string $uri * @throws Exception\Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri * @return string */ function calculateUri($uri) { if ($uri[0]!='/' && strpos($uri,'://')) { $uri = parse_url($uri,PHP_URL_PATH); } $uri = str_replace('//','/',$uri); if (strpos($uri,$this->getBaseUri())===0) { return trim(URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/'); // A special case, if the baseUri was accessed without a trailing // slash, we'll accept it as well. } elseif ($uri.'/' === $this->getBaseUri()) { return ''; } else { throw new Exception\Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')'); } } /** * Returns the HTTP depth header * * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY object * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent * * @param mixed $default * @return int */ function getHTTPDepth($default = self::DEPTH_INFINITY) { // If its not set, we'll grab the default $depth = $this->httpRequest->getHeader('Depth'); if (is_null($depth)) return $default; if ($depth == 'infinity') return self::DEPTH_INFINITY; // If its an unknown value. we'll grab the default if (!ctype_digit($depth)) return $default; return (int)$depth; } /** * Returns the HTTP range header * * This method returns null if there is no well-formed HTTP range request * header or array($start, $end). * * The first number is the offset of the first byte in the range. * The second number is the offset of the last byte in the range. * * If the second offset is null, it should be treated as the offset of the last byte of the entity * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity * * @return array|null */ function getHTTPRange() { $range = $this->httpRequest->getHeader('range'); if (is_null($range)) return null; // Matching "Range: bytes=1234-5678: both numbers are optional if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; if ($matches[1]==='' && $matches[2]==='') return null; return [ $matches[1]!==''?$matches[1]:null, $matches[2]!==''?$matches[2]:null, ]; } /** * Returns the HTTP Prefer header information. * * The prefer header is defined in: * http://tools.ietf.org/html/draft-snell-http-prefer-14 * * This method will return an array with options. * * Currently, the following options may be returned: * [ * 'return-asynch' => true, * 'return-minimal' => true, * 'return-representation' => true, * 'wait' => 30, * 'strict' => true, * 'lenient' => true, * ] * * This method also supports the Brief header, and will also return * 'return-minimal' if the brief header was set to 't'. * * For the boolean options, false will be returned if the headers are not * specified. For the integer options it will be 'null'. * * @return array */ function getHTTPPrefer() { $result = [ 'return-asynch' => false, 'return-minimal' => false, 'return-representation' => false, 'wait' => null, 'strict' => false, 'lenient' => false, ]; if ($prefer = $this->httpRequest->getHeader('Prefer')) { $parameters = array_map('trim', explode(',', $prefer) ); foreach($parameters as $parameter) { // Right now our regex only supports the tokens actually // specified in the draft. We may need to expand this if new // tokens get registered. if(!preg_match('/^(?P[a-z0-9-]+)(?:=(?P[0-9]+))?$/', $parameter, $matches)) { continue; } switch($matches['token']) { case 'return-asynch' : case 'return-minimal' : case 'return-representation' : case 'strict' : case 'lenient' : $result[$matches['token']] = true; break; case 'wait' : $result[$matches['token']] = $matches['value']; break; } } } elseif ($this->httpRequest->getHeader('Brief')=='t') { $result['return-minimal'] = true; } return $result; } /** * Returns information about Copy and Move requests * * This function is created to help getting information about the source and the destination for the * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions * * The returned value is an array with the following keys: * * destination - Destination path * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten) * * @param RequestInterface $request * @throws Exception\BadRequest upon missing or broken request headers * @throws Exception\UnsupportedMediaType when trying to copy into a * non-collection. * @throws Exception\PreconditionFailed If overwrite is set to false, but * the destination exists. * @throws Exception\Forbidden when source and destination paths are * identical. * @throws Exception\Conflict When trying to copy a node into its own * subtree. * @return array */ function getCopyAndMoveInfo(RequestInterface $request) { // Collecting the relevant HTTP headers if (!$request->getHeader('Destination')) throw new Exception\BadRequest('The destination header was not supplied'); $destination = $this->calculateUri($request->getHeader('Destination')); $overwrite = $request->getHeader('Overwrite'); if (!$overwrite) $overwrite = 'T'; if (strtoupper($overwrite)=='T') $overwrite = true; elseif (strtoupper($overwrite)=='F') $overwrite = false; // We need to throw a bad request exception, if the header was invalid else throw new Exception\BadRequest('The HTTP Overwrite header should be either T or F'); list($destinationDir) = URLUtil::splitPath($destination); try { $destinationParent = $this->tree->getNodeForPath($destinationDir); if (!($destinationParent instanceof ICollection)) throw new Exception\UnsupportedMediaType('The destination node is not a collection'); } catch (Exception\NotFound $e) { // If the destination parent node is not found, we throw a 409 throw new Exception\Conflict('The destination node is not found'); } try { $destinationNode = $this->tree->getNodeForPath($destination); // If this succeeded, it means the destination already exists // we'll need to throw precondition failed in case overwrite is false if (!$overwrite) throw new Exception\PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite'); } catch (Exception\NotFound $e) { // Destination didn't exist, we're all good $destinationNode = false; } $requestPath = $request->getPath(); if ($destination===$requestPath) { throw new Exception\Forbidden('Source and destination uri are identical.'); } if (substr($destination, 0, strlen($requestPath)+1) === $requestPath . '/') { throw new Exception\Conflict('The destination may not be part of the same subtree as the source path.'); } // These are the three relevant properties we need to return return [ 'destination' => $destination, 'destinationExists' => !!$destinationNode, 'destinationNode' => $destinationNode, ]; } /** * Returns a list of properties for a path * * This is a simplified version getPropertiesForPath. * if you aren't interested in status codes, but you just * want to have a flat list of properties. Use this method. * * @param string $path * @param array $propertyNames */ function getProperties($path, $propertyNames) { $result = $this->getPropertiesForPath($path,$propertyNames,0); return $result[0][200]; } /** * A kid-friendly way to fetch properties for a node's children. * * The returned array will be indexed by the path of the of child node. * Only properties that are actually found will be returned. * * The parent node will not be returned. * * @param string $path * @param array $propertyNames * @return array */ function getPropertiesForChildren($path, $propertyNames) { $result = []; foreach($this->getPropertiesForPath($path,$propertyNames,1) as $k=>$row) { // Skipping the parent path if ($k === 0) continue; $result[$row['href']] = $row[200]; } return $result; } /** * Returns a list of HTTP headers for a particular resource * * The generated http headers are based on properties provided by the * resource. The method basically provides a simple mapping between * DAV property and HTTP header. * * The headers are intended to be used for HEAD and GET requests. * * @param string $path * @return array */ function getHTTPHeaders($path) { $propertyMap = [ '{DAV:}getcontenttype' => 'Content-Type', '{DAV:}getcontentlength' => 'Content-Length', '{DAV:}getlastmodified' => 'Last-Modified', '{DAV:}getetag' => 'ETag', ]; $properties = $this->getProperties($path,array_keys($propertyMap)); $headers = []; foreach($propertyMap as $property=>$header) { if (!isset($properties[$property])) continue; if (is_scalar($properties[$property])) { $headers[$header] = $properties[$property]; // GetLastModified gets special cased } elseif ($properties[$property] instanceof Property\GetLastModified) { $headers[$header] = HTTP\Util::toHTTPDate($properties[$property]->getTime()); } } return $headers; } /** * Small helper to support PROPFIND with DEPTH_INFINITY. */ private function addPathNodesRecursively(&$propFindRequests, PropFind $propFind) { $newDepth = $propFind->getDepth(); $path = $propFind->getPath(); if ($newDepth !== self::DEPTH_INFINITY) { $newDepth--; } foreach($this->tree->getChildren($path) as $childNode) { $subPropFind = clone $propFind; $subPropFind->setDepth($newDepth); $subPath = $path !== ''? $path . '/' . $childNode->getName() : $childNode->getName(); $subPropFind->setPath($subPath); $propFindRequests[] = [ $subPropFind, $childNode ]; if (($newDepth===self::DEPTH_INFINITY || $newDepth>=1) && $childNode instanceof ICollection) { $this->addPathNodesRecursively($propFindRequests, $subPropFind); } } } /** * Returns a list of properties for a given path * * The path that should be supplied should have the baseUrl stripped out * The list of properties should be supplied in Clark notation. If the list is empty * 'allprops' is assumed. * * If a depth of 1 is requested child elements will also be returned. * * @param string $path * @param array $propertyNames * @param int $depth * @return array */ function getPropertiesForPath($path, $propertyNames = [], $depth = 0) { // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled if (!$this->enablePropfindDepthInfinity && $depth != 0) $depth = 1; $path = trim($path,'/'); $propFindType = $propertyNames?PropFind::NORMAL:PropFind::ALLPROPS; $propFind = new PropFind($path, $propertyNames, $depth, $propFindType); $parentNode = $this->tree->getNodeForPath($path); $propFindRequests = [[ $propFind, $parentNode ]]; if (($depth > 0 || $depth === self::DEPTH_INFINITY) && $parentNode instanceof ICollection) { $this->addPathNodesRecursively($propFindRequests, $propFind); } $returnPropertyList = []; foreach($propFindRequests as $propFindRequest) { list($propFind, $node) = $propFindRequest; $r = $this->getPropertiesByNode($propFind, $node); if ($r) { $result = $propFind->getResultForMultiStatus(); $result['href'] = $propFind->getPath(); // WebDAV recommends adding a slash to the path, if the path is // a collection. // Furthermore, iCal also demands this to be the case for // principals. This is non-standard, but we support it. $resourceType = $this->getResourceTypeForNode($node); if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { $result['href'].='/'; } $returnPropertyList[] = $result; } } return $returnPropertyList; } /** * Returns a list of properties for a list of paths. * * The path that should be supplied should have the baseUrl stripped out * The list of properties should be supplied in Clark notation. If the list is empty * 'allprops' is assumed. * * The result is returned as an array, with paths for it's keys. * The result may be returned out of order. * * @param array $paths * @param array $propertyNames * @return array */ function getPropertiesForMultiplePaths(array $paths, array $propertyNames = []) { $result = [ ]; $nodes = $this->tree->getMultipleNodes($paths); foreach($nodes as $path=>$node) { $propFind = new PropFind($path, $propertyNames); $r = $this->getPropertiesByNode($propFind,$node); if ($r) { $result[$path] = $propFind->getResultForMultiStatus(); $result[$path]['href'] = $path; $resourceType = $this->getResourceTypeForNode($node); if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { $result[$path]['href'].='/'; } } } return $result; } /** * Determines all properties for a node. * * This method tries to grab all properties for a node. This method is used * internally getPropertiesForPath and a few others. * * It could be useful to call this, if you already have an instance of your * target node and simply want to run through the system to get a correct * list of properties. * * @param PropFind $propFind * @param INode $node * @return bool */ function getPropertiesByNode(PropFind $propFind, INode $node) { return $this->emit('propFind', [$propFind, $node]); } /** * This method is invoked by sub-systems creating a new file. * * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin). * It was important to get this done through a centralized function, * allowing plugins to intercept this using the beforeCreateFile event. * * This method will return true if the file was actually created * * @param string $uri * @param resource $data * @param string $etag * @return bool */ function createFile($uri,$data, &$etag = null) { list($dir,$name) = URLUtil::splitPath($uri); if (!$this->emit('beforeBind',[$uri])) return false; $parent = $this->tree->getNodeForPath($dir); if (!$parent instanceof ICollection) { throw new Exception\Conflict('Files can only be created as children of collections'); } // It is possible for an event handler to modify the content of the // body, before it gets written. If this is the case, $modified // should be set to true. // // If $modified is true, we must not send back an etag. $modified = false; if (!$this->emit('beforeCreateFile',[$uri, &$data, $parent, &$modified])) return false; $etag = $parent->createFile($name,$data); if ($modified) $etag = null; $this->tree->markDirty($dir . '/' . $name); $this->emit('afterBind',[$uri]); $this->emit('afterCreateFile',[$uri, $parent]); return true; } /** * This method is invoked by sub-systems updating a file. * * This method will return true if the file was actually updated * * @param string $uri * @param resource $data * @param string $etag * @return bool */ function updateFile($uri,$data, &$etag = null) { $node = $this->tree->getNodeForPath($uri); // It is possible for an event handler to modify the content of the // body, before it gets written. If this is the case, $modified // should be set to true. // // If $modified is true, we must not send back an etag. $modified = false; if (!$this->emit('beforeWriteContent',[$uri, $node, &$data, &$modified])) return false; $etag = $node->put($data); if ($modified) $etag = null; $this->emit('afterWriteContent',[$uri, $node]); return true; } /** * This method is invoked by sub-systems creating a new directory. * * @param string $uri * @return void */ function createDirectory($uri) { $this->createCollection($uri,['{DAV:}collection'], []); } /** * Use this method to create a new collection * * The {DAV:}resourcetype is specified using the resourceType array. * At the very least it must contain {DAV:}collection. * * The properties array can contain a list of additional properties. * * @param string $uri The new uri * @param array $resourceType The resourceType(s) * @param array $properties A list of properties * @return array|null */ function createCollection($uri, array $resourceType, array $properties) { list($parentUri,$newName) = URLUtil::splitPath($uri); // Making sure {DAV:}collection was specified as resourceType if (!in_array('{DAV:}collection', $resourceType)) { throw new Exception\InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection'); } // Making sure the parent exists try { $parent = $this->tree->getNodeForPath($parentUri); } catch (Exception\NotFound $e) { throw new Exception\Conflict('Parent node does not exist'); } // Making sure the parent is a collection if (!$parent instanceof ICollection) { throw new Exception\Conflict('Parent node is not a collection'); } // Making sure the child does not already exist try { $parent->getChild($newName); // If we got here.. it means there's already a node on that url, and we need to throw a 405 throw new Exception\MethodNotAllowed('The resource you tried to create already exists'); } catch (Exception\NotFound $e) { // This is correct } if (!$this->emit('beforeBind',[$uri])) return; // There are 2 modes of operation. The standard collection // creates the directory, and then updates properties // the extended collection can create it directly. if ($parent instanceof IExtendedCollection) { $parent->createExtendedCollection($newName, $resourceType, $properties); } else { // No special resourcetypes are supported if (count($resourceType)>1) { throw new Exception\InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.'); } $parent->createDirectory($newName); $rollBack = false; $exception = null; $errorResult = null; if (count($properties)>0) { try { $errorResult = $this->updateProperties($uri, $properties); if (!isset($errorResult[200])) { $rollBack = true; } } catch (Exception $e) { $rollBack = true; $exception = $e; } } if ($rollBack) { if (!$this->emit('beforeUnbind',[$uri])) return; $this->tree->delete($uri); // Re-throwing exception if ($exception) throw $exception; // Re-arranging the result so it makes sense for // generateMultiStatus. $newResult = [ 'href' => $uri, ]; foreach($errorResult as $property=>$code) { if (!isset($newResult[$code])) { $newResult[$code] = [$property => null]; } else { $newResult[$code][$property] = null; } } return $newResult; } } $this->tree->markDirty($parentUri); $this->emit('afterBind',[$uri]); } /** * This method updates a resource's properties * * The properties array must be a list of properties. Array-keys are * property names in clarknotation, array-values are it's values. * If a property must be deleted, the value should be null. * * Note that this request should either completely succeed, or * completely fail. * * The response is an array with properties for keys, and http status codes * as their values. * * @param string $path * @param array $properties * @return array */ function updateProperties($path, array $properties) { $propPatch = new PropPatch($properties); $this->emit('propPatch', [$path, $propPatch]); $propPatch->commit(); return $propPatch->getResult(); } /** * This method checks the main HTTP preconditions. * * Currently these are: * * If-Match * * If-None-Match * * If-Modified-Since * * If-Unmodified-Since * * The method will return true if all preconditions are met * The method will return false, or throw an exception if preconditions * failed. If false is returned the operation should be aborted, and * the appropriate HTTP response headers are already set. * * Normally this method will throw 412 Precondition Failed for failures * related to If-None-Match, If-Match and If-Unmodified Since. It will * set the status to 304 Not Modified for If-Modified_since. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function checkPreconditions(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $node = null; $lastMod = null; $etag = null; if ($ifMatch = $request->getHeader('If-Match')) { // If-Match contains an entity tag. Only if the entity-tag // matches we are allowed to make the request succeed. // If the entity-tag is '*' we are only allowed to make the // request succeed if a resource exists at that url. try { $node = $this->tree->getNodeForPath($path); } catch (Exception\NotFound $e) { throw new Exception\PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match'); } // Only need to check entity tags if they are not * if ($ifMatch!=='*') { // There can be multiple etags $ifMatch = explode(',',$ifMatch); $haveMatch = false; foreach($ifMatch as $ifMatchItem) { // Stripping any extra spaces $ifMatchItem = trim($ifMatchItem,' '); $etag = $node->getETag(); if ($etag===$ifMatchItem) { $haveMatch = true; } else { // Evolution has a bug where it sometimes prepends the " // with a \. This is our workaround. if (str_replace('\\"','"', $ifMatchItem) === $etag) { $haveMatch = true; } } } if (!$haveMatch) { if ($etag) $response->setHeader('ETag', $etag); throw new Exception\PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match'); } } } if ($ifNoneMatch = $request->getHeader('If-None-Match')) { // The If-None-Match header contains an etag. // Only if the ETag does not match the current ETag, the request will succeed // The header can also contain *, in which case the request // will only succeed if the entity does not exist at all. $nodeExists = true; if (!$node) { try { $node = $this->tree->getNodeForPath($path); } catch (Exception\NotFound $e) { $nodeExists = false; } } if ($nodeExists) { $haveMatch = false; if ($ifNoneMatch==='*') $haveMatch = true; else { // There might be multiple etags $ifNoneMatch = explode(',', $ifNoneMatch); $etag = $node->getETag(); foreach($ifNoneMatch as $ifNoneMatchItem) { // Stripping any extra spaces $ifNoneMatchItem = trim($ifNoneMatchItem,' '); if ($etag===$ifNoneMatchItem) $haveMatch = true; } } if ($haveMatch) { if ($etag) $response->setHeader('ETag', $etag); if ($request->getMethod()==='GET') { $response->setStatus(304); return false; } else { throw new Exception\PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).','If-None-Match'); } } } } if (!$ifNoneMatch && ($ifModifiedSince = $request->getHeader('If-Modified-Since'))) { // The If-Modified-Since header contains a date. We // will only return the entity if it has been changed since // that date. If it hasn't been changed, we return a 304 // header // Note that this header only has to be checked if there was no If-None-Match header // as per the HTTP spec. $date = HTTP\Util::parseHTTPDate($ifModifiedSince); if ($date) { if (is_null($node)) { $node = $this->tree->getNodeForPath($path); } $lastMod = $node->getLastModified(); if ($lastMod) { $lastMod = new \DateTime('@' . $lastMod); if ($lastMod <= $date) { $response->setStatus(304); $response->setHeader('Last-Modified', HTTP\Util::toHTTPDate($lastMod)); return false; } } } } if ($ifUnmodifiedSince = $request->getHeader('If-Unmodified-Since')) { // The If-Unmodified-Since will allow allow the request if the // entity has not changed since the specified date. $date = HTTP\Util::parseHTTPDate($ifUnmodifiedSince); // We must only check the date if it's valid if ($date) { if (is_null($node)) { $node = $this->tree->getNodeForPath($path); } $lastMod = $node->getLastModified(); if ($lastMod) { $lastMod = new \DateTime('@' . $lastMod); if ($lastMod > $date) { throw new Exception\PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since'); } } } } // Now the hardest, the If: header. The If: header can contain multiple // urls, etags and so-called 'state tokens'. // // Examples of state tokens include lock-tokens (as defined in rfc4918) // and sync-tokens (as defined in rfc6578). // // The only proper way to deal with these, is to emit events, that a // Sync and Lock plugin can pick up. $ifConditions = $this->getIfConditions($request); foreach($ifConditions as $kk => $ifCondition) { foreach($ifCondition['tokens'] as $ii => $token) { $ifConditions[$kk]['tokens'][$ii]['validToken'] = false; } } // Plugins are responsible for validating all the tokens. // If a plugin deemed a token 'valid', it will set 'validToken' to // true. $this->emit('validateTokens', [ $request, &$ifConditions ]); // Now we're going to analyze the result. // Every ifCondition needs to validate to true, so we exit as soon as // we have an invalid condition. foreach($ifConditions as $ifCondition) { $uri = $ifCondition['uri']; $tokens = $ifCondition['tokens']; // We only need 1 valid token for the condition to succeed. foreach($tokens as $token) { $tokenValid = $token['validToken'] || !$token['token']; $etagValid = false; if (!$token['etag']) { $etagValid = true; } // Checking the etag, only if the token was already deamed // valid and there is one. if ($token['etag'] && $tokenValid) { // The token was valid, and there was an etag.. We must // grab the current etag and check it. $node = $this->tree->getNodeForPath($uri); $etagValid = $node instanceof IFile && $node->getETag() == $token['etag']; } if (($tokenValid && $etagValid) ^ $token['negate']) { // Both were valid, so we can go to the next condition. continue 2; } } // If we ended here, it means there was no valid etag + token // combination found for the current condition. This means we fail! throw new Exception\PreconditionFailed('Failed to find a valid token/etag combination for ' . $uri, 'If'); } return true; } /** * This method is created to extract information from the WebDAV HTTP 'If:' header * * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information * The function will return an array, containing structs with the following keys * * * uri - the uri the condition applies to. * * tokens - The lock token. another 2 dimensional array containing 3 elements * * Example 1: * * If: () * * Would result in: * * [ * [ * 'uri' => '/request/uri', * 'tokens' => [ * [ * [ * 'negate' => false, * 'token' => 'opaquelocktoken:181d4fae-7d8c-11d0-a765-00a0c91e6bf2', * 'etag' => "" * ] * ] * ], * ] * ] * * Example 2: * * If: (Not ["Im An ETag"]) (["Another ETag"]) (Not ["Path2 ETag"]) * * Would result in: * * [ * [ * 'uri' => 'path', * 'tokens' => [ * [ * [ * 'negate' => true, * 'token' => 'opaquelocktoken:181d4fae-7d8c-11d0-a765-00a0c91e6bf2', * 'etag' => '"Im An ETag"' * ], * [ * 'negate' => false, * 'token' => '', * 'etag' => '"Another ETag"' * ] * ] * ], * ], * [ * 'uri' => 'path2', * 'tokens' => [ * [ * [ * 'negate' => true, * 'token' => '', * 'etag' => '"Path2 ETag"' * ] * ] * ], * ], * ] * * @return array */ function getIfConditions(RequestInterface $request) { $header = $request->getHeader('If'); if (!$header) return []; $matches = []; $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; preg_match_all($regex,$header,$matches,PREG_SET_ORDER); $conditions = []; foreach($matches as $match) { // If there was no uri specified in this match, and there were // already conditions parsed, we add the condition to the list of // conditions for the previous uri. if (!$match['uri'] && count($conditions)) { $conditions[count($conditions)-1]['tokens'][] = [ 'negate' => $match['not']?true:false, 'token' => $match['token'], 'etag' => isset($match['etag'])?$match['etag']:'' ]; } else { if (!$match['uri']) { $realUri = $request->getPath(); } else { $realUri = $this->calculateUri($match['uri']); } $conditions[] = [ 'uri' => $realUri, 'tokens' => [ [ 'negate' => $match['not']?true:false, 'token' => $match['token'], 'etag' => isset($match['etag'])?$match['etag']:'' ] ], ]; } } return $conditions; } /** * Returns an array with resourcetypes for a node. * * @param INode $node * @return array */ function getResourceTypeForNode(INode $node) { $result = []; foreach($this->resourceTypeMapping as $className => $resourceType) { if ($node instanceof $className) $result[] = $resourceType; } return $result; } // }}} // {{{ XML Readers & Writers /** * Generates a WebDAV propfind response body based on a list of nodes. * * If 'strip404s' is set to true, all 404 responses will be removed. * * @param array $fileProperties The list with nodes * @param bool strip404s * @return string */ function generateMultiStatus(array $fileProperties, $strip404s = false) { $dom = new \DOMDocument('1.0','utf-8'); //$dom->formatOutput = true; $multiStatus = $dom->createElement('d:multistatus'); $dom->appendChild($multiStatus); // Adding in default namespaces foreach($this->xmlNamespaces as $namespace=>$prefix) { $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); } foreach($fileProperties as $entry) { $href = $entry['href']; unset($entry['href']); if ($strip404s && isset($entry[404])) { unset($entry[404]); } $response = new Property\Response($href,$entry); $response->serialize($this,$multiStatus); } return $dom->saveXML(); } /** * This method parses a PropPatch request * * PropPatch changes the properties for a resource. This method * returns a list of properties. * * The keys in the returned array contain the property name (e.g.: {DAV:}displayname, * and the value contains the property value. If a property is to be removed the value * will be null. * * @param string $body xml body * @return array list of properties in need of updating or deletion */ function parsePropPatchRequest($body) { //We'll need to change the DAV namespace declaration to something else in order to make it parsable $dom = XMLUtil::loadDOMDocument($body); $newProperties = []; foreach($dom->firstChild->childNodes as $child) { if ($child->nodeType !== XML_ELEMENT_NODE) continue; $operation = XMLUtil::toClarkNotation($child); if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue; $innerProperties = XMLUtil::parseProperties($child, $this->propertyMap); foreach($innerProperties as $propertyName=>$propertyValue) { if ($operation==='{DAV:}remove') { $propertyValue = null; } $newProperties[$propertyName] = $propertyValue; } } return $newProperties; } /** * This method parses the PROPFIND request and returns its information * * This will either be a list of properties, or an empty array; in which case * an {DAV:}allprop was requested. * * @param string $body * @return array */ function parsePropFindRequest($body) { // If the propfind body was empty, it means IE is requesting 'all' properties if (!$body) return []; $dom = XMLUtil::loadDOMDocument($body); $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); if (is_null($elem)) throw new Exception\UnsupportedMediaType('We could not find a {DAV:}propfind element in the xml request body'); return array_keys(XMLUtil::parseProperties($elem)); } // }}} } sabre-dav-2.1.10/lib/DAV/ServerPlugin.php000066400000000000000000000036151267035675400200010ustar00rootroot00000000000000name = $name; foreach($children as $child) { if (!($child instanceof INode)) throw new Exception('Only instances of Sabre\DAV\INode are allowed to be passed in the children argument'); $this->addChild($child); } } /** * Adds a new childnode to this collection * * @param INode $child * @return void */ function addChild(INode $child) { $this->children[$child->getName()] = $child; } /** * Returns the name of the collection * * @return string */ function getName() { return $this->name; } /** * Returns a child object, by its name. * * This method makes use of the getChildren method to grab all the child nodes, and compares the name. * Generally its wise to override this, as this can usually be optimized * * This method must throw Sabre\DAV\Exception\NotFound if the node does not * exist. * * @param string $name * @throws Exception\NotFound * @return INode */ function getChild($name) { if (isset($this->children[$name])) return $this->children[$name]; throw new Exception\NotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\''); } /** * Returns a list of children for this collection * * @return INode[] */ function getChildren() { return array_values($this->children); } } sabre-dav-2.1.10/lib/DAV/SimpleFile.php000066400000000000000000000045741267035675400174120ustar00rootroot00000000000000name = $name; $this->contents = $contents; $this->mimeType = $mimeType; } /** * Returns the node name for this file. * * This name is used to construct the url. * * @return string */ function getName() { return $this->name; } /** * Returns the data * * This method may either return a string or a readable stream resource * * @return mixed */ function get() { return $this->contents; } /** * Returns the size of the file, in bytes. * * @return int */ function getSize() { return strlen($this->contents); } /** * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. * * Return null if the ETag can not effectively be determined * @return string */ function getETag() { return '"' . md5($this->contents) . '"'; } /** * Returns the mime-type for a file * * If null is returned, we'll assume application/octet-stream * @return string */ function getContentType() { return $this->mimeType; } } sabre-dav-2.1.10/lib/DAV/StringUtil.php000066400000000000000000000051771267035675400174650ustar00rootroot00000000000000 'The current synctoken', * 'added' => [ * 'new.txt', * ], * 'modified' => [ * 'modified.txt', * ], * 'deleted' => array( * 'foo.php.bak', * 'old.txt' * ) * ]; * * The syncToken property should reflect the *current* syncToken of the * collection, as reported getSyncToken(). This is needed here too, to * ensure the operation is atomic. * * If the syncToken is specified as null, this is an initial sync, and all * members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The second argument is basically the 'depth' of the report. If it's 1, * you only have to report changes that happened only directly in immediate * descendants. If it's 2, it should also include changes from the nodes * below the child collections. (grandchildren) * * The third (optional) argument allows a client to specify how many * results should be returned at most. If the limit is not specified, it * should be treated as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ function getChanges($syncToken, $syncLevel, $limit = null); } sabre-dav-2.1.10/lib/DAV/Sync/Plugin.php000066400000000000000000000236031267035675400175250ustar00rootroot00000000000000server = $server; $self = $this; $server->on('report', function($reportName, $dom, $uri) use ($self) { if ($reportName === '{DAV:}sync-collection') { $this->server->transactionType = 'report-sync-collection'; $self->syncCollection($uri, $dom); return false; } }); $server->on('propFind', [$this, 'propFind']); $server->on('validateTokens', [$this, 'validateTokens']); } /** * 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 */ function getSupportedReportSet($uri) { $node = $this->server->tree->getNodeForPath($uri); if ($node instanceof ISyncCollection && $node->getSyncToken()) { return [ '{DAV:}sync-collection', ]; } return []; } /** * This method handles the {DAV:}sync-collection HTTP REPORT. * * @param string $uri * @param \DOMDocument $dom * @return void */ function syncCollection($uri, \DOMDocument $dom) { // rfc3253 specifies 0 is the default value for Depth: $depth = $this->server->getHTTPDepth(0); list( $syncToken, $syncLevel, $limit, $properties ) = $this->parseSyncCollectionRequest($dom, $depth); // Getting the data $node = $this->server->tree->getNodeForPath($uri); if (!$node instanceof ISyncCollection) { throw new DAV\Exception\ReportNotSupported('The {DAV:}sync-collection REPORT is not supported on this url.'); } $token = $node->getSyncToken(); if (!$token) { throw new DAV\Exception\ReportNotSupported('No sync information is available at this node'); } if (!is_null($syncToken)) { // Sync-token must start with our prefix if (substr($syncToken, 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); } $syncToken = substr($syncToken, strlen(self::SYNCTOKEN_PREFIX)); } $changeInfo = $node->getChanges($syncToken, $syncLevel, $limit); if (is_null($changeInfo)) { throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); } // Encoding the response $this->sendSyncCollectionResponse( $changeInfo['syncToken'], $uri, $changeInfo['added'], $changeInfo['modified'], $changeInfo['deleted'], $properties ); } /** * Parses the {DAV:}sync-collection REPORT request body. * * This method returns an array with 3 values: * 0 - the value of the {DAV:}sync-token element * 1 - the value of the {DAV:}sync-level element * 2 - The value of the {DAV:}limit element * 3 - A list of requested properties * * @param \DOMDocument $dom * @param int $depth * @return void */ protected function parseSyncCollectionRequest(\DOMDocument $dom, $depth) { $xpath = new \DOMXPath($dom); $xpath->registerNamespace('d','urn:DAV'); $syncToken = $xpath->query("//d:sync-token"); if ($syncToken->length !== 1) { throw new DAV\Exception\BadRequest('You must specify a {DAV:}sync-token element, and it must appear exactly once'); } $syncToken = $syncToken->item(0)->nodeValue; // Initial sync if (!$syncToken) $syncToken = null; $syncLevel = $xpath->query("//d:sync-level"); if ($syncLevel->length === 0) { // In case there was no sync-level, it could mean that we're dealing // with an old client. For these we must use the depth header // instead. $syncLevel = $depth; } else { $syncLevel = $syncLevel->item(0)->nodeValue; if ($syncLevel === 'infinite') { $syncLevel = DAV\Server::DEPTH_INFINITY; } } $limit = $xpath->query("//d:limit/d:nresults"); if ($limit->length === 0) { $limit = null; } else { $limit = $limit->item(0)->nodeValue; } $prop = $xpath->query('d:prop'); if ($prop->length !== 1) { throw new DAV\Exception\BadRequest('The {DAV:}sync-collection must contain extactly 1 {DAV:}prop'); } $properties = array_keys( DAV\XMLUtil::parseProperties($dom->documentElement) ); return [ $syncToken, $syncLevel, $limit, $properties, ]; } /** * Sends the response to a sync-collection request. * * @param string $syncToken * @param string $collectionUrl * @param array $added * @param array $modified * @param array $deleted * @param array $properties * @return void */ protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties) { $dom = new \DOMDocument('1.0','utf-8'); $dom->formatOutput = true; $multiStatus = $dom->createElement('d:multistatus'); $dom->appendChild($multiStatus); // Adding in default namespaces foreach($this->server->xmlNamespaces as $namespace=>$prefix) { $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); } $fullPaths = []; // Pre-fetching children, if this is possible. foreach(array_merge($added, $modified) as $item) { $fullPath = $collectionUrl . '/' . $item; $fullPaths[] = $fullPath; } foreach($this->server->getPropertiesForMultiplePaths($fullPaths, $properties) as $fullPath => $props) { // The 'Property_Response' class is responsible for generating a // single {DAV:}response xml element. $response = new DAV\Property\Response($fullPath, $props); $response->serialize($this->server, $multiStatus); } // Deleted items also show up as 'responses'. They have no properties, // and a single {DAV:}status element set as 'HTTP/1.1 404 Not Found'. foreach($deleted as $item) { $fullPath = $collectionUrl . '/' . $item; $response = new DAV\Property\Response($fullPath, [], 404); $response->serialize($this->server, $multiStatus); } $syncToken = $dom->createElement('d:sync-token', self::SYNCTOKEN_PREFIX . $syncToken); $multiStatus->appendChild($syncToken); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->setBody($dom->saveXML()); } /** * This method is triggered whenever properties are requested for a node. * We intercept this to see if we must return a {DAV:}sync-token. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @return void */ function propFind(DAV\PropFind $propFind, DAV\INode $node) { $propFind->handle('{DAV:}sync-token', function() use ($node) { if (!$node instanceof ISyncCollection || !$token = $node->getSyncToken()) { return; } return self::SYNCTOKEN_PREFIX . $token; }); } /** * The validateTokens event is triggered before every request. * * It's a moment where this plugin can check all the supplied lock tokens * in the If: header, and check if they are valid. * * @param mixed $conditions * @return void */ function validateTokens( RequestInterface $request, &$conditions ) { foreach($conditions as $kk=>$condition) { foreach($condition['tokens'] as $ii=>$token) { // Sync-tokens must always start with our designated prefix. if (substr($token['token'], 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { continue; } // Checking if the token is a match. $node = $this->server->tree->getNodeForPath($condition['uri']); if ( $node instanceof ISyncCollection && $node->getSyncToken() == substr($token['token'], strlen(self::SYNCTOKEN_PREFIX)) ) { $conditions[$kk]['tokens'][$ii]['validToken'] = true; } } } } } sabre-dav-2.1.10/lib/DAV/TemporaryFileFilterPlugin.php000066400000000000000000000210011267035675400224500ustar00rootroot00000000000000dataDir = $dataDir; } /** * Initialize the plugin * * This is called automatically be the Server class after this plugin is * added with Sabre\DAV\Server::addPlugin() * * @param Server $server * @return void */ function initialize(Server $server) { $this->server = $server; $server->on('beforeMethod', [$this,'beforeMethod']); $server->on('beforeCreateFile',[$this,'beforeCreateFile']); } /** * This method is called before any HTTP method handler * * This method intercepts any GET, DELETE, PUT and PROPFIND calls to * filenames that are known to match the 'temporary file' regex. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function beforeMethod(RequestInterface $request, ResponseInterface $response) { if (!$tempLocation = $this->isTempFile($request->getPath())) return true; switch($request->getMethod()) { case 'GET' : return $this->httpGet($request, $response, $tempLocation); case 'PUT' : return $this->httpPut($request, $response, $tempLocation); case 'PROPFIND' : return $this->httpPropfind($request, $response, $tempLocation); case 'DELETE' : return $this->httpDelete($request, $response, $tempLocation); } return true; } /** * This method is invoked if some subsystem creates a new file. * * This is used to deal with HTTP LOCK requests which create a new * file. * * @param string $uri * @param resource $data * @return bool */ function beforeCreateFile($uri,$data) { if ($tempPath = $this->isTempFile($uri)) { $hR = $this->server->httpResponse; $hR->setHeader('X-Sabre-Temp','true'); file_put_contents($tempPath,$data); return false; } return true; } /** * This method will check if the url matches the temporary file pattern * if it does, it will return an path based on $this->dataDir for the * temporary file storage. * * @param string $path * @return boolean|string */ protected function isTempFile($path) { // We're only interested in the basename. list(, $tempPath) = URLUtil::splitPath($path); foreach($this->temporaryFilePatterns as $tempFile) { if (preg_match($tempFile,$tempPath)) { return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile'; } } return false; } /** * This method handles the GET method for temporary files. * If the file doesn't exist, it will return false which will kick in * the regular system for the GET method. * * @param RequestInterface $request * @param ResponseInterface $hR * @param string $tempLocation * @return bool */ function httpGet(RequestInterface $request, ResponseInterface $hR, $tempLocation) { if (!file_exists($tempLocation)) return true; $hR->setHeader('Content-Type','application/octet-stream'); $hR->setHeader('Content-Length',filesize($tempLocation)); $hR->setHeader('X-Sabre-Temp','true'); $hR->setStatus(200); $hR->setBody(fopen($tempLocation,'r')); return false; } /** * This method handles the PUT method. * * @param RequestInterface $request * @param ResponseInterface $hR * @param string $tempLocation * @return bool */ function httpPut(RequestInterface $request, ResponseInterface $hR, $tempLocation) { $hR->setHeader('X-Sabre-Temp','true'); $newFile = !file_exists($tempLocation); if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) { throw new Exception\PreconditionFailed('The resource already exists, and an If-None-Match header was supplied'); } file_put_contents($tempLocation,$this->server->httpRequest->getBody()); $hR->setStatus($newFile?201:200); return false; } /** * This method handles the DELETE method. * * If the file didn't exist, it will return false, which will make the * standard HTTP DELETE handler kick in. * * @param RequestInterface $request * @param ResponseInterface $hR * @param string $tempLocation * @return bool */ function httpDelete(RequestInterface $request, ResponseInterface $hR, $tempLocation) { if (!file_exists($tempLocation)) return true; unlink($tempLocation); $hR->setHeader('X-Sabre-Temp','true'); $hR->setStatus(204); return false; } /** * This method handles the PROPFIND method. * * It's a very lazy method, it won't bother checking the request body * for which properties were requested, and just sends back a default * set of properties. * * @param RequestInterface $request * @param ResponseInterface $hR * @param string $tempLocation * @return bool */ function httpPropfind(RequestInterface $request, ResponseInterface $hR, $tempLocation) { if (!file_exists($tempLocation)) return true; $hR->setHeader('X-Sabre-Temp','true'); $hR->setStatus(207); $hR->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->parsePropFindRequest($request->getBodyAsString()); $properties = [ 'href' => $request->getPath(), 200 => [ '{DAV:}getlastmodified' => new Property\GetLastModified(filemtime($tempLocation)), '{DAV:}getcontentlength' => filesize($tempLocation), '{DAV:}resourcetype' => new Property\ResourceType(null), '{'.Server::NS_SABREDAV.'}tempFile' => true, ], ]; $data = $this->server->generateMultiStatus([$properties]); $hR->setBody($data); return false; } /** * This method returns the directory where the temporary files should be stored. * * @return string */ protected function getDataDir() { return $this->dataDir; } } sabre-dav-2.1.10/lib/DAV/Tree.php000066400000000000000000000224211267035675400162470ustar00rootroot00000000000000rootNode = $rootNode; } /** * Returns the INode object for the requested path * * @param string $path * @return INode */ function getNodeForPath($path) { $path = trim($path,'/'); if (isset($this->cache[$path])) return $this->cache[$path]; // Is it the root node? if (!strlen($path)) { return $this->rootNode; } // Attempting to fetch its parent list($parentName, $baseName) = URLUtil::splitPath($path); // If there was no parent, we must simply ask it from the root node. if ($parentName==="") { $node = $this->rootNode->getChild($baseName); } else { // Otherwise, we recursively grab the parent and ask him/her. $parent = $this->getNodeForPath($parentName); if (!($parent instanceof ICollection)) throw new Exception\NotFound('Could not find node at path: ' . $path); $node = $parent->getChild($baseName); } $this->cache[$path] = $node; return $node; } /** * This function allows you to check if a node exists. * * Implementors of this class should override this method to make * it cheaper. * * @param string $path * @return bool */ function nodeExists($path) { try { // The root always exists if ($path==='') return true; list($parent, $base) = URLUtil::splitPath($path); $parentNode = $this->getNodeForPath($parent); if (!$parentNode instanceof ICollection) return false; return $parentNode->childExists($base); } catch (Exception\NotFound $e) { return false; } } /** * Copies a file from path to another * * @param string $sourcePath The source location * @param string $destinationPath The full destination path * @return void */ function copy($sourcePath, $destinationPath) { $sourceNode = $this->getNodeForPath($sourcePath); // grab the dirname and basename components list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); $destinationParent = $this->getNodeForPath($destinationDir); $this->copyNode($sourceNode,$destinationParent,$destinationName); $this->markDirty($destinationDir); } /** * Moves a file from one location to another * * @param string $sourcePath The path to the file which should be moved * @param string $destinationPath The full destination path, so not just the destination parent node * @return int */ function move($sourcePath, $destinationPath) { list($sourceDir) = URLUtil::splitPath($sourcePath); list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); if ($sourceDir===$destinationDir) { // If this is a 'local' rename, it means we can just trigger a rename. $sourceNode = $this->getNodeForPath($sourcePath); $sourceNode->setName($destinationName); } else { $newParentNode = $this->getNodeForPath($destinationDir); $moveSuccess = false; if ($newParentNode instanceof IMoveTarget) { // The target collection may be able to handle the move $sourceNode = $this->getNodeForPath($sourcePath); $moveSuccess = $newParentNode->moveInto($destinationName, $sourcePath, $sourceNode); } if (!$moveSuccess) { $this->copy($sourcePath,$destinationPath); $this->getNodeForPath($sourcePath)->delete(); } } $this->markDirty($sourceDir); $this->markDirty($destinationDir); } /** * Deletes a node from the tree * * @param string $path * @return void */ function delete($path) { $node = $this->getNodeForPath($path); $node->delete(); list($parent) = URLUtil::splitPath($path); $this->markDirty($parent); } /** * Returns a list of childnodes for a given path. * * @param string $path * @return array */ function getChildren($path) { $node = $this->getNodeForPath($path); $children = $node->getChildren(); $basePath = trim($path, '/'); if ($basePath !== '') $basePath.='/'; foreach ($children as $child) { $this->cache[$basePath . $child->getName()] = $child; } return $children; } /** * This method is called with every tree update * * Examples of tree updates are: * * node deletions * * node creations * * copy * * move * * renaming nodes * * If Tree classes implement a form of caching, this will allow * them to make sure caches will be expired. * * If a path is passed, it is assumed that the entire subtree is dirty * * @param string $path * @return void */ function markDirty($path) { // We don't care enough about sub-paths // flushing the entire cache $path = trim($path,'/'); foreach($this->cache as $nodePath=>$node) { if ($nodePath == $path || strpos($nodePath,$path.'/')===0) unset($this->cache[$nodePath]); } } /** * This method tells the tree system to pre-fetch and cache a list of * children of a single parent. * * There are a bunch of operations in the WebDAV stack that request many * children (based on uris), and sometimes fetching many at once can * optimize this. * * This method returns an array with the found nodes. It's keys are the * original paths. The result may be out of order. * * @param array $paths List of nodes that must be fetched. * @return array */ function getMultipleNodes($paths) { // Finding common parents $parents = []; foreach($paths as $path) { list($parent, $node) = URLUtil::splitPath($path); if (!isset($parents[$parent])) { $parents[$parent] = [$node]; } else { $parents[$parent][] = $node; } } $result = []; foreach($parents as $parent=>$children) { $parentNode = $this->getNodeForPath($parent); if ($parentNode instanceof IMultiGet) { foreach($parentNode->getMultipleChildren($children) as $childNode) { $fullPath = $parent . '/' . $childNode->getName(); $result[$fullPath] = $childNode; $this->cache[$fullPath] = $childNode; } } else { foreach($children as $child) { $fullPath = $parent . '/' . $child; $result[$fullPath] = $this->getNodeForPath($fullPath); } } } return $result; } /** * copyNode * * @param INode $source * @param ICollection $destinationParent * @param string $destinationName * @return void */ protected function copyNode(INode $source, ICollection $destinationParent, $destinationName = null) { if (!$destinationName) $destinationName = $source->getName(); if ($source instanceof IFile) { $data = $source->get(); // If the body was a string, we need to convert it to a stream if (is_string($data)) { $stream = fopen('php://temp','r+'); fwrite($stream,$data); rewind($stream); $data = $stream; } $destinationParent->createFile($destinationName,$data); $destination = $destinationParent->getChild($destinationName); } elseif ($source instanceof ICollection) { $destinationParent->createDirectory($destinationName); $destination = $destinationParent->getChild($destinationName); foreach($source->getChildren() as $child) { $this->copyNode($child,$destination); } } if ($source instanceof IProperties && $destination instanceof IProperties) { $props = $source->getProperties([]); $propPatch = new PropPatch($props); $destination->propPatch($propPatch); $propPatch->commit(); } } } sabre-dav-2.1.10/lib/DAV/URLUtil.php000066400000000000000000000007461267035675400166560ustar00rootroot00000000000000 * will be returned as: * {http://www.example.org}myelem * * This format is used throughout the SabreDAV sourcecode. * Elements encoded with the urn:DAV namespace will * be returned as if they were in the DAV: namespace. This is to avoid * compatibility problems. * * This function will return null if a nodetype other than an Element is passed. * * @param \DOMNode $dom * @return string */ static function toClarkNotation(\DOMNode $dom) { if ($dom->nodeType !== XML_ELEMENT_NODE) return null; // Mapping back to the real namespace, in case it was dav if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI; // Mapping to clark notation return '{' . $ns . '}' . $dom->localName; } /** * Parses a clark-notation string, and returns the namespace and element * name components. * * If the string was invalid, it will throw an InvalidArgumentException. * * @param string $str * @throws InvalidArgumentException * @return array */ static function parseClarkNotation($str) { if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) { throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); } return [ $matches[1], $matches[2] ]; } /** * This method takes an XML document (as string) and converts all instances of the * DAV: namespace to urn:DAV * * This is unfortunately needed, because the DAV: namespace violates the xml namespaces * spec, and causes the DOM to throw errors * * @param string $xmlDocument * @return array|string|null */ static function convertDAVNamespace($xmlDocument) { // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV: // namespace is actually a violation of the XML namespaces specification, and will cause errors return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument); } /** * This method provides a generic way to load a DOMDocument for WebDAV use. * * This method throws a Sabre\DAV\Exception\BadRequest exception for any xml errors. * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. * * @param string $xml * @throws Sabre\DAV\Exception\BadRequest * @return DOMDocument */ static function loadDOMDocument($xml) { if (empty($xml)) throw new Exception\BadRequest('Empty XML document sent'); // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower) // does not support this, so we must intercept this and convert to UTF-8. if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") { // Note: the preceeding byte sequence is "]*)encoding="UTF-16"([^>]*)>|u','',$xml); } // Retaining old error setting $oldErrorSetting = libxml_use_internal_errors(true); // Fixes an XXE vulnerability on PHP versions older than 5.3.23 or // 5.4.13. $oldEntityLoaderSetting = libxml_disable_entity_loader(true); // Clearing any previous errors libxml_clear_errors(); $dom = new \DOMDocument(); // We don't generally care about any whitespace $dom->preserveWhiteSpace = false; $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR); if ($error = libxml_get_last_error()) { libxml_clear_errors(); throw new Exception\BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')'); } // Restoring old mechanism for error handling if ($oldErrorSetting===false) libxml_use_internal_errors(false); if ($oldEntityLoaderSetting===false) libxml_disable_entity_loader(false); return $dom; } /** * Parses all WebDAV properties out of a DOM Element * * Generally WebDAV properties are enclosed in {DAV:}prop elements. This * method helps by going through all these and pulling out the actual * propertynames, making them array keys and making the property values, * well.. the array values. * * If no value was given (self-closing element) null will be used as the * value. This is used in for example PROPFIND requests. * * Complex values are supported through the propertyMap argument. The * propertyMap should have the clark-notation properties as it's keys, and * classnames as values. * * When any of these properties are found, the unserialize() method will be * (statically) called. The result of this method is used as the value. * * @param \DOMElement $parentNode * @param array $propertyMap * @return array */ static function parseProperties(\DOMElement $parentNode, array $propertyMap = []) { $propList = []; foreach($parentNode->childNodes as $propNode) { if (self::toClarkNotation($propNode)!=='{DAV:}prop') continue; foreach($propNode->childNodes as $propNodeData) { /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */ if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue; $propertyName = self::toClarkNotation($propNodeData); if (isset($propertyMap[$propertyName])) { $propList[$propertyName] = call_user_func([$propertyMap[$propertyName],'unserialize'],$propNodeData, $propertyMap); } else { $propList[$propertyName] = $propNodeData->textContent; } } } return $propList; } } sabre-dav-2.1.10/lib/DAVACL/000077500000000000000000000000001267035675400151565ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAVACL/AbstractPrincipalCollection.php000066400000000000000000000122131267035675400233070ustar00rootroot00000000000000principalPrefix = $principalPrefix; $this->principalBackend = $principalBackend; } /** * 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 $principalInfo * @return IPrincipal */ abstract function getChildForPrincipal(array $principalInfo); /** * Returns the name of this collection. * * @return string */ function getName() { list(,$name) = URLUtil::splitPath($this->principalPrefix); return $name; } /** * Return the list of users * * @return array */ function getChildren() { if ($this->disableListing) throw new DAV\Exception\MethodNotAllowed('Listing members of this collection is disabled'); $children = []; foreach($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) { $children[] = $this->getChildForPrincipal($principalInfo); } return $children; } /** * Returns a child object, by its name. * * @param string $name * @throws DAV\Exception\NotFound * @return IPrincipal */ function getChild($name) { $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name); if (!$principalInfo) throw new DAV\Exception\NotFound('Principal with name ' . $name . ' not found'); return $this->getChildForPrincipal($principalInfo); } /** * This method is used to search for principals matching a set of * properties. * * This search is specifically used by RFC3744's principal-property-search * REPORT. You should at least allow searching on * http://sabredav.org/ns}email-address. * * The actual search should be a unicode-non-case-sensitive search. The * keys in searchProperties are the WebDAV property names, while the values * are the property values to search on. * * By default, if multiple properties are submitted to this method, the * various properties should be combined with 'AND'. If $test is set to * 'anyof', it should be combined using 'OR'. * * This method should simply return a list of 'child names', which may be * used to call $this->getChild in the future. * * @param array $searchProperties * @return array */ function searchPrincipals(array $searchProperties, $test = 'allof') { $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties, $test); $r = []; foreach($result as $row) { list(, $r[]) = URLUtil::splitPath($row); } return $r; } /** * Finds a principal by its URI. * * This method may receive any type of uri, but mailto: addresses will be * the most common. * * Implementation of this API is optional. It is currently used by the * CalDAV system to find principals based on their email addresses. If this * API is not implemented, some features may not work correctly. * * This method must return a relative principal path, or null, if the * principal was not found or you refuse to find it. * * @param string $uri * @return string */ function findByUri($uri) { return $this->principalBackend->findByUri($uri, $this->principalPrefix); } } sabre-dav-2.1.10/lib/DAVACL/Exception/000077500000000000000000000000001267035675400171145ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAVACL/Exception/AceConflict.php000066400000000000000000000015201267035675400217750ustar00rootroot00000000000000ownerDocument; $np = $doc->createElementNS('DAV:','d:no-ace-conflict'); $errorNode->appendChild($np); } } sabre-dav-2.1.10/lib/DAVACL/Exception/NeedPrivileges.php000066400000000000000000000037641267035675400225440ustar00rootroot00000000000000uri = $uri; $this->privileges = $privileges; parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"'); } /** * Adds in extra information in the xml response. * * This method adds the {DAV:}need-privileges element as defined in rfc3744 * * @param DAV\Server $server * @param \DOMElement $errorNode * @return void */ function serialize(DAV\Server $server,\DOMElement $errorNode) { $doc = $errorNode->ownerDocument; $np = $doc->createElementNS('DAV:','d:need-privileges'); $errorNode->appendChild($np); foreach($this->privileges as $privilege) { $resource = $doc->createElementNS('DAV:','d:resource'); $np->appendChild($resource); $resource->appendChild($doc->createElementNS('DAV:','d:href',$server->getBaseUri() . $this->uri)); $priv = $doc->createElementNS('DAV:','d:privilege'); $resource->appendChild($priv); preg_match('/^{([^}]*)}(.*)$/',$privilege,$privilegeParts); $priv->appendChild($doc->createElementNS($privilegeParts[1],'d:' . $privilegeParts[2])); } } } sabre-dav-2.1.10/lib/DAVACL/Exception/NoAbstract.php000066400000000000000000000015321267035675400216660ustar00rootroot00000000000000ownerDocument; $np = $doc->createElementNS('DAV:','d:no-abstract'); $errorNode->appendChild($np); } } sabre-dav-2.1.10/lib/DAVACL/Exception/NotRecognizedPrincipal.php000066400000000000000000000016111267035675400242400ustar00rootroot00000000000000ownerDocument; $np = $doc->createElementNS('DAV:','d:recognized-principal'); $errorNode->appendChild($np); } } sabre-dav-2.1.10/lib/DAVACL/Exception/NotSupportedPrivilege.php000066400000000000000000000015741267035675400241510ustar00rootroot00000000000000ownerDocument; $np = $doc->createElementNS('DAV:','d:not-supported-privilege'); $errorNode->appendChild($np); } } sabre-dav-2.1.10/lib/DAVACL/IACL.php000066400000000000000000000035111267035675400163770ustar00rootroot00000000000000getChild in the future. * * @param array $searchProperties * @param string $test * @return array */ function searchPrincipals(array $searchProperties, $test = 'allof'); /** * Finds a principal by its URI. * * This method may receive any type of uri, but mailto: addresses will be * the most common. * * Implementation of this API is optional. It is currently used by the * CalDAV system to find principals based on their email addresses. If this * API is not implemented, some features may not work correctly. * * This method must return a relative principal path, or null, if the * principal was not found or you refuse to find it. * * @param string $uri * @return string */ function findByUri($uri); } sabre-dav-2.1.10/lib/DAVACL/Plugin.php000066400000000000000000001264751267035675400171440ustar00rootroot00000000000000 'Display name', '{http://sabredav.org/ns}email-address' => 'Email address', ]; /** * Any principal uri's added here, will automatically be added to the list * of ACL's. They will effectively receive {DAV:}all privileges, as a * protected privilege. * * @var array */ public $adminPrincipals = []; /** * Returns a list of features added by this plugin. * * This list is used in the response of a HTTP OPTIONS request. * * @return array */ function getFeatures() { return ['access-control', 'calendarserver-principal-property-search']; } /** * Returns a list of available methods for a given url * * @param string $uri * @return array */ function getMethods($uri) { return ['ACL']; } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using Sabre\DAV\Server::getPlugin * * @return string */ function getPluginName() { return 'acl'; } /** * 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 */ function getSupportedReportSet($uri) { return [ '{DAV:}expand-property', '{DAV:}principal-property-search', '{DAV:}principal-search-property-set', ]; } /** * Checks if the current user has the specified privilege(s). * * You can specify a single privilege, or a list of privileges. * This method will throw an exception if the privilege is not available * and return true otherwise. * * @param string $uri * @param array|string $privileges * @param int $recursion * @param bool $throwExceptions if set to false, this method won't throw exceptions. * @throws Sabre\DAVACL\Exception\NeedPrivileges * @return bool */ function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) { if (!is_array($privileges)) $privileges = [$privileges]; $acl = $this->getCurrentUserPrivilegeSet($uri); if (is_null($acl)) { if ($this->allowAccessToNodesWithoutACL) { return true; } else { if ($throwExceptions) throw new Exception\NeedPrivileges($uri,$privileges); else return false; } } $failed = []; foreach($privileges as $priv) { if (!in_array($priv, $acl)) { $failed[] = $priv; } } if ($failed) { if ($throwExceptions) throw new Exception\NeedPrivileges($uri,$failed); else return false; } return true; } /** * Returns the standard users' principal. * * This is one authorative principal url for the current user. * This method will return null if the user wasn't logged in. * * @return string|null */ function getCurrentUserPrincipal() { $authPlugin = $this->server->getPlugin('auth'); if (is_null($authPlugin)) return null; /** @var $authPlugin Sabre\DAV\Auth\Plugin */ $userName = $authPlugin->getCurrentUser(); if (!$userName) return null; return $this->defaultUsernamePath . '/' . $userName; } /** * Returns a list of principals that's associated to the current * user, either directly or through group membership. * * @return array */ function getCurrentUserPrincipals() { $currentUser = $this->getCurrentUserPrincipal(); if (is_null($currentUser)) return []; return array_merge( [$currentUser], $this->getPrincipalMembership($currentUser) ); } /** * This array holds a cache for all the principals that are associated with * a single principal. * * @var array */ protected $principalMembershipCache = []; /** * Returns all the principal groups the specified principal is a member of. * * @param string $principal * @return array */ function getPrincipalMembership($mainPrincipal) { // First check our cache if (isset($this->principalMembershipCache[$mainPrincipal])) { return $this->principalMembershipCache[$mainPrincipal]; } $check = [$mainPrincipal]; $principals = []; while(count($check)) { $principal = array_shift($check); $node = $this->server->tree->getNodeForPath($principal); if ($node instanceof IPrincipal) { foreach($node->getGroupMembership() as $groupMember) { if (!in_array($groupMember, $principals)) { $check[] = $groupMember; $principals[] = $groupMember; } } } } // Store the result in the cache $this->principalMembershipCache[$mainPrincipal] = $principals; return $principals; } /** * Returns the supported privilege structure for this ACL plugin. * * See RFC3744 for more details. Currently we default on a simple, * standard structure. * * You can either get the list of privileges by a uri (path) or by * specifying a Node. * * @param string|INode $node * @return array */ function getSupportedPrivilegeSet($node) { if (is_string($node)) { $node = $this->server->tree->getNodeForPath($node); } if ($node instanceof IACL) { $result = $node->getSupportedPrivilegeSet(); if ($result) return $result; } return self::getDefaultSupportedPrivilegeSet(); } /** * Returns a fairly standard set of privileges, which may be useful for * other systems to use as a basis. * * @return array */ static function getDefaultSupportedPrivilegeSet() { return [ 'privilege' => '{DAV:}all', 'abstract' => true, 'aggregates' => [ [ 'privilege' => '{DAV:}read', 'aggregates' => [ [ 'privilege' => '{DAV:}read-acl', 'abstract' => false, ], [ 'privilege' => '{DAV:}read-current-user-privilege-set', 'abstract' => false, ], ], ], // {DAV:}read [ 'privilege' => '{DAV:}write', 'aggregates' => [ [ 'privilege' => '{DAV:}write-acl', 'abstract' => false, ], [ 'privilege' => '{DAV:}write-properties', 'abstract' => false, ], [ 'privilege' => '{DAV:}write-content', 'abstract' => false, ], [ 'privilege' => '{DAV:}bind', 'abstract' => false, ], [ 'privilege' => '{DAV:}unbind', 'abstract' => false, ], [ 'privilege' => '{DAV:}unlock', 'abstract' => false, ], ], ], // {DAV:}write ], ]; // {DAV:}all } /** * Returns the supported privilege set as a flat list * * This is much easier to parse. * * The returned list will be index by privilege name. * The value is a struct containing the following properties: * - aggregates * - abstract * - concrete * * @param string|INode $node * @return array */ final function getFlatPrivilegeSet($node) { $privs = $this->getSupportedPrivilegeSet($node); $flat = []; $this->getFPSTraverse($privs, null, $flat); return $flat; } /** * Traverses the privilege set tree for reordering * * This function is solely used by getFlatPrivilegeSet, and would have been * a closure if it wasn't for the fact I need to support PHP 5.2. * * @param array $priv * @param $concrete * @param array $flat * @return void */ final private function getFPSTraverse($priv, $concrete, &$flat) { $myPriv = [ 'privilege' => $priv['privilege'], 'abstract' => isset($priv['abstract']) && $priv['abstract'], 'aggregates' => [], 'concrete' => isset($priv['abstract']) && $priv['abstract']?$concrete:$priv['privilege'], ]; if (isset($priv['aggregates'])) foreach($priv['aggregates'] as $subPriv) $myPriv['aggregates'][] = $subPriv['privilege']; $flat[$priv['privilege']] = $myPriv; if (isset($priv['aggregates'])) { foreach($priv['aggregates'] as $subPriv) { $this->getFPSTraverse($subPriv, $myPriv['concrete'], $flat); } } } /** * Returns the full ACL list. * * Either a uri or a INode may be passed. * * null will be returned if the node doesn't support ACLs. * * @param string|DAV\INode $node * @return array */ function getACL($node) { if (is_string($node)) { $node = $this->server->tree->getNodeForPath($node); } if (!$node instanceof IACL) { return null; } $acl = $node->getACL(); foreach($this->adminPrincipals as $adminPrincipal) { $acl[] = [ 'principal' => $adminPrincipal, 'privilege' => '{DAV:}all', 'protected' => true, ]; } return $acl; } /** * Returns a list of privileges the current user has * on a particular node. * * Either a uri or a DAV\INode may be passed. * * null will be returned if the node doesn't support ACLs. * * @param string|DAV\INode $node * @return array */ function getCurrentUserPrivilegeSet($node) { if (is_string($node)) { $node = $this->server->tree->getNodeForPath($node); } $acl = $this->getACL($node); if (is_null($acl)) return null; $principals = $this->getCurrentUserPrincipals(); $collected = []; foreach($acl as $ace) { $principal = $ace['principal']; switch($principal) { case '{DAV:}owner' : $owner = $node->getOwner(); if ($owner && in_array($owner, $principals)) { $collected[] = $ace; } break; // 'all' matches for every user case '{DAV:}all' : // 'authenticated' matched for every user that's logged in. // Since it's not possible to use ACL while not being logged // in, this is also always true. case '{DAV:}authenticated' : $collected[] = $ace; break; // 'unauthenticated' can never occur either, so we simply // ignore these. case '{DAV:}unauthenticated' : break; default : if (in_array($ace['principal'], $principals)) { $collected[] = $ace; } break; } } // Now we deduct all aggregated privileges. $flat = $this->getFlatPrivilegeSet($node); $collected2 = []; while(count($collected)) { $current = array_pop($collected); $collected2[] = $current['privilege']; foreach($flat[$current['privilege']]['aggregates'] as $subPriv) { $collected2[] = $subPriv; $collected[] = $flat[$subPriv]; } } return array_values(array_unique($collected2)); } /** * Returns a principal url based on an email address. * * Note that wether or not this works may depend on wether a search * facility is built into the server. * * This method returns false if the principal could not be found. * * @deprecated use getPrincipalByUri instead. * @return string|bool */ function getPrincipalByEmail($email) { $result = $this->getPrincipalByUri('mailto:' . $email); return $result?:false; } /** * Returns a principal based on its uri. * * Returns null if the principal could not be found. * * @param string $uri * @return null|string */ function getPrincipalByUri($uri) { $result = null; $collections = $this->principalCollectionSet; foreach($collections as $collection) { $principalCollection = $this->server->tree->getNodeForPath($collection); if (!$principalCollection instanceof IPrincipalCollection) { // Not a principal collection, we're simply going to ignore // this. continue; } $result = $principalCollection->findByUri($uri); if ($result) { return $result; } } } /** * Principal property search * * This method can search for principals matching certain values in * properties. * * This method will return a list of properties for the matched properties. * * @param array $searchProperties The properties to search on. This is a * key-value list. The keys are property * names, and the values the strings to * match them on. * @param array $requestedProperties This is the list of properties to * return for every match. * @param string $collectionUri The principal collection to search on. * If this is ommitted, the standard * principal collection-set will be used. * @param string $test "allof" to use AND to search the * properties. 'anyof' for OR. * @return array This method returns an array structure similar to * Sabre\DAV\Server::getPropertiesForPath. Returned * properties are index by a HTTP status code. * */ function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null, $test = 'allof') { if (!is_null($collectionUri)) { $uris = [$collectionUri]; } else { $uris = $this->principalCollectionSet; } $lookupResults = []; foreach($uris as $uri) { $principalCollection = $this->server->tree->getNodeForPath($uri); if (!$principalCollection instanceof IPrincipalCollection) { // Not a principal collection, we're simply going to ignore // this. continue; } $results = $principalCollection->searchPrincipals($searchProperties, $test); foreach($results as $result) { $lookupResults[] = rtrim($uri,'/') . '/' . $result; } } $matches = []; foreach($lookupResults as $lookupResult) { list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0); } return $matches; } /** * Sets up the plugin * * This method is automatically called by the server class. * * @param DAV\Server $server * @return void */ function initialize(DAV\Server $server) { $this->server = $server; $server->on('propFind', [$this,'propFind'], 20); $server->on('beforeMethod', [$this,'beforeMethod'],20); $server->on('beforeBind', [$this,'beforeBind'],20); $server->on('beforeUnbind', [$this,'beforeUnbind'],20); $server->on('propPatch', [$this,'propPatch']); $server->on('beforeUnlock', [$this,'beforeUnlock'],20); $server->on('report', [$this,'report']); $server->on('method:ACL', [$this,'httpAcl']); array_push($server->protectedProperties, '{DAV:}alternate-URI-set', '{DAV:}principal-URL', '{DAV:}group-membership', '{DAV:}principal-collection-set', '{DAV:}current-user-principal', '{DAV:}supported-privilege-set', '{DAV:}current-user-privilege-set', '{DAV:}acl', '{DAV:}acl-restrictions', '{DAV:}inherited-acl-set', '{DAV:}owner', '{DAV:}group' ); // Automatically mapping nodes implementing IPrincipal to the // {DAV:}principal resourcetype. $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal'; // Mapping the group-member-set property to the HrefList property // class. $server->propertyMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Property\\HrefList'; } /* {{{ Event handlers */ /** * Triggered before any method is handled * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function beforeMethod(RequestInterface $request, ResponseInterface $response) { $method = $request->getMethod(); $path = $request->getPath(); $exists = $this->server->tree->nodeExists($path); // If the node doesn't exists, none of these checks apply if (!$exists) return; switch($method) { case 'GET' : case 'HEAD' : case 'OPTIONS' : // For these 3 we only need to know if the node is readable. $this->checkPrivileges($path,'{DAV:}read'); break; case 'PUT' : case 'LOCK' : case 'UNLOCK' : // This method requires the write-content priv if the node // already exists, and bind on the parent if the node is being // created. // The bind privilege is handled in the beforeBind event. $this->checkPrivileges($path,'{DAV:}write-content'); break; case 'PROPPATCH' : $this->checkPrivileges($path,'{DAV:}write-properties'); break; case 'ACL' : $this->checkPrivileges($path,'{DAV:}write-acl'); break; case 'COPY' : case 'MOVE' : // Copy requires read privileges on the entire source tree. // If the target exists write-content normally needs to be // checked, however, we're deleting the node beforehand and // creating a new one after, so this is handled by the // beforeUnbind event. // // The creation of the new node is handled by the beforeBind // event. // // If MOVE is used beforeUnbind will also be used to check if // the sourcenode can be deleted. $this->checkPrivileges($path,'{DAV:}read',self::R_RECURSIVE); break; } } /** * Triggered before a new node is created. * * This allows us to check permissions for any operation that creates a * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE. * * @param string $uri * @return void */ function beforeBind($uri) { list($parentUri) = URLUtil::splitPath($uri); $this->checkPrivileges($parentUri,'{DAV:}bind'); } /** * Triggered before a node is deleted * * This allows us to check permissions for any operation that will delete * an existing node. * * @param string $uri * @return void */ function beforeUnbind($uri) { list($parentUri) = URLUtil::splitPath($uri); $this->checkPrivileges($parentUri,'{DAV:}unbind',self::R_RECURSIVEPARENTS); } /** * Triggered before a node is unlocked. * * @param string $uri * @param DAV\Locks\LockInfo $lock * @TODO: not yet implemented * @return void */ function beforeUnlock($uri, DAV\Locks\LockInfo $lock) { } /** * Triggered before properties are looked up in specific nodes. * * @param DAV\PropFind $propFind * @param DAV\INode $node * @param array $requestedProperties * @param array $returnedProperties * @TODO really should be broken into multiple methods, or even a class. * @return bool */ function propFind(DAV\PropFind $propFind, DAV\INode $node) { $path = $propFind->getPath(); // Checking the read permission if (!$this->checkPrivileges($path,'{DAV:}read',self::R_PARENT,false)) { // User is not allowed to read properties // Returning false causes the property-fetching system to pretend // that the node does not exist, and will cause it to be hidden // from listings such as PROPFIND or the browser plugin. if ($this->hideNodesFromListings) { return false; } // Otherwise we simply mark every property as 403. foreach($propFind->getRequestedProperties() as $requestedProperty) { $propFind->set($requestedProperty, null, 403); } return; } /* Adding principal properties */ if ($node instanceof IPrincipal) { $propFind->handle('{DAV:}alternate-URI-set', function() use ($node) { return new DAV\Property\HrefList($node->getAlternateUriSet()); }); $propFind->handle('{DAV:}principal-URL', function() use ($node) { return new DAV\Property\Href($node->getPrincipalUrl() . '/'); }); $propFind->handle('{DAV:}group-member-set', function() use ($node) { $members = $node->getGroupMemberSet(); foreach($members as $k=>$member) { $members[$k] = rtrim($member,'/') . '/'; } return new DAV\Property\HrefList($members); }); $propFind->handle('{DAV:}group-membership', function() use ($node) { $members = $node->getGroupMembership(); foreach($members as $k=>$member) { $members[$k] = rtrim($member,'/') . '/'; } return new DAV\Property\HrefList($members); }); $propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']); } $propFind->handle('{DAV:}principal-collection-set', function() { $val = $this->principalCollectionSet; // Ensuring all collections end with a slash foreach($val as $k=>$v) $val[$k] = $v . '/'; return new DAV\Property\HrefList($val); }); $propFind->handle('{DAV:}current-user-principal', function() { if ($url = $this->getCurrentUserPrincipal()) { return new Property\Principal(Property\Principal::HREF, $url . '/'); } else { return new Property\Principal(Property\Principal::UNAUTHENTICATED); } }); $propFind->handle('{DAV:}supported-privilege-set', function() use ($node) { return new Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node)); }); $propFind->handle('{DAV:}current-user-privilege-set', function() use ($node, $propFind, $path) { if (!$this->checkPrivileges($path, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) { $propFind->set('{DAV:}current-user-privilege-set', null, 403); } else { $val = $this->getCurrentUserPrivilegeSet($node); if (!is_null($val)) { return new Property\CurrentUserPrivilegeSet($val); } } }); $propFind->handle('{DAV:}acl', function() use ($node, $propFind, $path) { /* The ACL property contains all the permissions */ if (!$this->checkPrivileges($path, '{DAV:}read-acl', self::R_PARENT, false)) { $propFind->set('{DAV:}acl', null, 403); } else { $acl = $this->getACL($node); if (!is_null($acl)) { return new Property\Acl($this->getACL($node)); } } }); $propFind->handle('{DAV:}acl-restrictions', function() { return new Property\AclRestrictions(); }); /* Adding ACL properties */ if ($node instanceof IACL) { $propFind->handle('{DAV:}owner', function() use ($node) { return new DAV\Property\Href($node->getOwner() . '/'); }); } } /** * This method intercepts PROPPATCH methods and make sure the * group-member-set is updated correctly. * * @param string $path * @param DAV\PropPatch $propPatch * @return void */ function propPatch($path, DAV\PropPatch $propPatch) { $propPatch->handle('{DAV:}group-member-set', function($value) use ($path) { if (is_null($value)) { $memberSet = []; } elseif ($value instanceof DAV\Property\HrefList) { $memberSet = array_map( [$this->server,'calculateUri'], $value->getHrefs() ); } else { throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null'); } $node = $this->server->tree->getNodeForPath($path); if (!($node instanceof IPrincipal)) { // Fail return false; } $node->setGroupMemberSet($memberSet); // We must also clear our cache, just in case $this->principalMembershipCache = []; return true; }); } /** * This method handles HTTP REPORT requests * * @param string $reportName * @param \DOMNode $dom * @return bool */ function report($reportName, $dom) { switch($reportName) { case '{DAV:}principal-property-search' : $this->server->transactionType = 'report-principal-property-search'; $this->principalPropertySearchReport($dom); return false; case '{DAV:}principal-search-property-set' : $this->server->transactionType = 'report-principal-search-property-set'; $this->principalSearchPropertySetReport($dom); return false; case '{DAV:}expand-property' : $this->server->transactionType = 'report-expand-property'; $this->expandPropertyReport($dom); return false; } } /** * This method is responsible for handling the 'ACL' event. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpAcl(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $body = $request->getBodyAsString(); $dom = DAV\XMLUtil::loadDOMDocument($body); $newAcl = Property\Acl::unserialize($dom->firstChild, $this->server->propertyMap) ->getPrivileges(); // Normalizing urls foreach($newAcl as $k=>$newAce) { $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']); } $node = $this->server->tree->getNodeForPath($path); if (!($node instanceof IACL)) { throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method'); } $oldAcl = $this->getACL($node); $supportedPrivileges = $this->getFlatPrivilegeSet($node); /* Checking if protected principals from the existing principal set are not overwritten. */ foreach($oldAcl as $oldAce) { if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; $found = false; foreach($newAcl as $newAce) { if ( $newAce['privilege'] === $oldAce['privilege'] && $newAce['principal'] === $oldAce['principal'] && $newAce['protected'] ) $found = true; } if (!$found) throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request'); } foreach($newAcl as $newAce) { // Do we recognize the privilege if (!isset($supportedPrivileges[$newAce['privilege']])) { throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server'); } if ($supportedPrivileges[$newAce['privilege']]['abstract']) { throw new Exception\NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege'); } // Looking up the principal try { $principal = $this->server->tree->getNodeForPath($newAce['principal']); } catch (DAV\Exception\NotFound $e) { throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist'); } if (!($principal instanceof IPrincipal)) { throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal'); } } $node->setACL($newAcl); $response->setStatus(200); // Breaking the event chain, because we handled this method. return false; } /* }}} */ /* Reports {{{ */ /** * The expand-property report is defined in RFC3253 section 3-8. * * This report is very similar to a standard PROPFIND. The difference is * that it has the additional ability to look at properties containing a * {DAV:}href element, follow that property and grab additional elements * there. * * Other rfc's, such as ACL rely on this report, so it made sense to put * it in this plugin. * * @param \DOMElement $dom * @return void */ protected function expandPropertyReport($dom) { $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild); $depth = $this->server->getHTTPDepth(0); $requestUri = $this->server->getRequestUri(); $result = $this->expandProperties($requestUri,$requestedProperties,$depth); $dom = new \DOMDocument('1.0','utf-8'); $dom->formatOutput = true; $multiStatus = $dom->createElement('d:multistatus'); $dom->appendChild($multiStatus); // Adding in default namespaces foreach($this->server->xmlNamespaces as $namespace=>$prefix) { $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); } foreach($result as $response) { $response->serialize($this->server, $multiStatus); } $xml = $dom->saveXML(); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setBody($xml); } /** * This method is used by expandPropertyReport to parse * out the entire HTTP request. * * @param \DOMElement $node * @return array */ protected function parseExpandPropertyReportRequest($node) { $requestedProperties = []; do { if (DAV\XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue; if ($node->firstChild) { $children = $this->parseExpandPropertyReportRequest($node->firstChild); } else { $children = []; } $namespace = $node->getAttribute('namespace'); if (!$namespace) $namespace = 'DAV:'; $propName = '{'.$namespace.'}' . $node->getAttribute('name'); $requestedProperties[$propName] = $children; } while ($node = $node->nextSibling); return $requestedProperties; } /** * This method expands all the properties and returns * a list with property values * * @param array $path * @param array $requestedProperties the list of required properties * @param int $depth * @return array */ protected function expandProperties($path, array $requestedProperties, $depth) { $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth); $result = []; foreach($foundProperties as $node) { foreach($requestedProperties as $propertyName=>$childRequestedProperties) { // We're only traversing if sub-properties were requested if(count($childRequestedProperties)===0) continue; // We only have to do the expansion if the property was found // and it contains an href element. if (!array_key_exists($propertyName,$node[200])) continue; if ($node[200][$propertyName] instanceof DAV\Property\IHref) { $hrefs = [$node[200][$propertyName]->getHref()]; } elseif ($node[200][$propertyName] instanceof DAV\Property\HrefList) { $hrefs = $node[200][$propertyName]->getHrefs(); } $childProps = []; foreach($hrefs as $href) { $childProps = array_merge($childProps, $this->expandProperties($href, $childRequestedProperties, 0)); } $node[200][$propertyName] = new DAV\Property\ResponseList($childProps); } $result[] = new DAV\Property\Response($node['href'], $node); } return $result; } /** * principalSearchPropertySetReport * * This method responsible for handing the * {DAV:}principal-search-property-set report. This report returns a list * of properties the client may search on, using the * {DAV:}principal-property-search report. * * @param \DOMDocument $dom * @return void */ protected function principalSearchPropertySetReport(\DOMDocument $dom) { $httpDepth = $this->server->getHTTPDepth(0); if ($httpDepth!==0) { throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0'); } if ($dom->firstChild->hasChildNodes()) throw new DAV\Exception\BadRequest('The principal-search-property-set report element is not allowed to have child elements'); $dom = new \DOMDocument('1.0','utf-8'); $dom->formatOutput = true; $root = $dom->createElement('d:principal-search-property-set'); $dom->appendChild($root); // Adding in default namespaces foreach($this->server->xmlNamespaces as $namespace=>$prefix) { $root->setAttribute('xmlns:' . $prefix,$namespace); } $nsList = $this->server->xmlNamespaces; foreach($this->principalSearchPropertySet as $propertyName=>$description) { $psp = $dom->createElement('d:principal-search-property'); $root->appendChild($psp); $prop = $dom->createElement('d:prop'); $psp->appendChild($prop); $propName = null; preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]); $prop->appendChild($currentProperty); $descriptionElem = $dom->createElement('d:description'); $descriptionElem->setAttribute('xml:lang','en'); $descriptionElem->appendChild($dom->createTextNode($description)); $psp->appendChild($descriptionElem); } $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->setStatus(200); $this->server->httpResponse->setBody($dom->saveXML()); } /** * principalPropertySearchReport * * This method is responsible for handing the * {DAV:}principal-property-search report. This report can be used for * clients to search for groups of principals, based on the value of one * or more properties. * * @param \DOMDocument $dom * @return void */ protected function principalPropertySearchReport(\DOMDocument $dom) { list( $searchProperties, $requestedProperties, $applyToPrincipalCollectionSet, $test ) = $this->parsePrincipalPropertySearchReportRequest($dom); $uri = null; if (!$applyToPrincipalCollectionSet) { $uri = $this->server->getRequestUri(); } $result = $this->principalSearch($searchProperties, $requestedProperties, $uri, $test); $prefer = $this->server->getHTTPPRefer(); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); $this->server->httpResponse->setHeader('Vary','Brief,Prefer'); $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); } /** * parsePrincipalPropertySearchReportRequest * * This method parses the request body from a * {DAV:}principal-property-search report. * * This method returns an array with two elements: * 1. an array with properties to search on, and their values * 2. a list of propertyvalues that should be returned for the request. * * @param \DOMDocument $dom * @return array */ protected function parsePrincipalPropertySearchReportRequest($dom) { $httpDepth = $this->server->getHTTPDepth(0); if ($httpDepth!==0) { throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0'); } $searchProperties = []; $applyToPrincipalCollectionSet = false; $test = $dom->firstChild->getAttribute('test') === 'anyof' ? 'anyof' : 'allof'; // Parsing the search request foreach($dom->firstChild->childNodes as $searchNode) { if (DAV\XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set') { $applyToPrincipalCollectionSet = true; } if (DAV\XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search') continue; $propertyName = null; $propertyValue = null; foreach($searchNode->childNodes as $childNode) { switch(DAV\XMLUtil::toClarkNotation($childNode)) { case '{DAV:}prop' : $property = DAV\XMLUtil::parseProperties($searchNode); reset($property); $propertyName = key($property); break; case '{DAV:}match' : $propertyValue = $childNode->textContent; break; } } if (is_null($propertyName) || is_null($propertyValue)) throw new DAV\Exception\BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue); $searchProperties[$propertyName] = $propertyValue; } return [ $searchProperties, array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet, $test ]; } /* }}} */ } sabre-dav-2.1.10/lib/DAVACL/Principal.php000066400000000000000000000155071267035675400176200ustar00rootroot00000000000000principalBackend = $principalBackend; $this->principalProperties = $principalProperties; } /** * Returns the full principal url * * @return string */ function getPrincipalUrl() { return $this->principalProperties['uri']; } /** * Returns a list of alternative urls for a principal * * This can for example be an email address, or ldap url. * * @return array */ function getAlternateUriSet() { $uris = []; if (isset($this->principalProperties['{DAV:}alternate-URI-set'])) { $uris = $this->principalProperties['{DAV:}alternate-URI-set']; } if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) { $uris[] = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']; } return array_unique($uris); } /** * 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 */ function getGroupMemberSet() { return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']); } /** * 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 */ function getGroupMembership() { return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']); } /** * 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 $groupMembers * @return void */ function setGroupMemberSet(array $groupMembers) { $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers); } /** * Returns this principals name. * * @return string */ function getName() { $uri = $this->principalProperties['uri']; list(, $name) = URLUtil::splitPath($uri); return $name; } /** * Returns the name of the user * * @return string */ function getDisplayName() { if (isset($this->principalProperties['{DAV:}displayname'])) { return $this->principalProperties['{DAV:}displayname']; } else { return $this->getName(); } } /** * Returns a list of properties * * @param array $requestedProperties * @return array */ function getProperties($requestedProperties) { $newProperties = []; foreach($requestedProperties as $propName) { if (isset($this->principalProperties[$propName])) { $newProperties[$propName] = $this->principalProperties[$propName]; } } return $newProperties; } /** * Updates properties on this node. * * This method received a PropPatch object, which contains all the * information about the update. * * To update specific properties, call the 'handle' method on this object. * Read the PropPatch documentation for more information. * * @param DAV\PropPatch $propPatch * @return void */ function propPatch(DAV\PropPatch $propPatch) { return $this->principalBackend->updatePrincipal( $this->principalProperties['uri'], $propPatch ); } /** * Returns the owner principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ function getOwner() { return $this->principalProperties['uri']; } /** * Returns a group principal * * This must be a url to a principal, or null if there's no owner * * @return string|null */ 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 */ function getACL() { return [ [ 'privilege' => '{DAV:}read', 'principal' => '{DAV:}authenticated', 'protected' => true, ], ]; } /** * Updates the ACL * * This method will receive a list of new ACE's. * * @param array $acl * @return void */ function setACL(array $acl) { throw new DAV\Exception\MethodNotAllowed('Updating ACLs is not allowed here'); } /** * 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 */ function getSupportedPrivilegeSet() { return null; } } sabre-dav-2.1.10/lib/DAVACL/PrincipalBackend/000077500000000000000000000000001267035675400203475ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAVACL/PrincipalBackend/AbstractBackend.php000066400000000000000000000030721267035675400240750ustar00rootroot00000000000000searchPrincipals( $principalPrefix, ['{http://sabredav.org/ns}email-address' => substr($uri,7)] ); if ($result) { return $result[0]; } } } sabre-dav-2.1.10/lib/DAVACL/PrincipalBackend/BackendInterface.php000066400000000000000000000112101267035675400242230ustar00rootroot00000000000000 [ 'dbField' => 'displayname', ], /** * This property is actually used by the CardDAV plugin, where it gets * mapped to {http://calendarserver.orgi/ns/}me-card. * * The reason we don't straight-up use that property, is because * me-card is defined as a property on the users' addressbook * collection. */ '{http://sabredav.org/ns}vcard-url' => [ 'dbField' => 'vcardurl', ], /** * This is the users' primary email-address. */ '{http://sabredav.org/ns}email-address' =>[ 'dbField' => 'email', ], ]; /** * Sets up the backend. * * @param PDO $pdo * @param string $tableName * @param string $groupMembersTableName * @deprecated We are removing the tableName arguments in a future version * of sabredav. Use the public properties instead. */ function __construct(\PDO $pdo, $tableName = 'principals', $groupMembersTableName = 'groupmembers') { $this->pdo = $pdo; $this->tableName = $tableName; $this->groupMembersTableName = $groupMembersTableName; } /** * Returns a list of principals based on a prefix. * * This prefix will often contain something like 'principals'. You are only * expected to return principals that are in this base path. * * You are expected to return at least a 'uri' for every user, you can * return any additional properties if you wish so. Common properties are: * {DAV:}displayname * {http://sabredav.org/ns}email-address - This is a custom SabreDAV * field that's actualy injected in a number of other properties. If * you have an email address, use this property. * * @param string $prefixPath * @return array */ function getPrincipalsByPrefix($prefixPath) { $fields = [ 'uri', ]; foreach($this->fieldMap as $key=>$value) { $fields[] = $value['dbField']; } $result = $this->pdo->query('SELECT '.implode(',', $fields).' FROM '. $this->tableName); $principals = []; while($row = $result->fetch(\PDO::FETCH_ASSOC)) { // Checking if the principal is in the prefix list($rowPrefix) = URLUtil::splitPath($row['uri']); if ($rowPrefix !== $prefixPath) continue; $principal = [ 'uri' => $row['uri'], ]; foreach($this->fieldMap as $key=>$value) { if ($row[$value['dbField']]) { $principal[$key] = $row[$value['dbField']]; } } $principals[] = $principal; } return $principals; } /** * Returns a specific principal, specified by it's path. * The returned structure should be the exact same as from * getPrincipalsByPrefix. * * @param string $path * @return array */ function getPrincipalByPath($path) { $fields = [ 'id', 'uri', ]; foreach($this->fieldMap as $key=>$value) { $fields[] = $value['dbField']; } $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '. $this->tableName . ' WHERE uri = ?'); $stmt->execute([$path]); $row = $stmt->fetch(\PDO::FETCH_ASSOC); if (!$row) return; $principal = [ 'id' => $row['id'], 'uri' => $row['uri'], ]; foreach($this->fieldMap as $key=>$value) { if ($row[$value['dbField']]) { $principal[$key] = $row[$value['dbField']]; } } return $principal; } /** * Updates one ore more webdav properties on a principal. * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documenation for more info and examples. * * @param string $path * @param \Sabre\DAV\PropPatch $propPatch */ function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) { $propPatch->handle(array_keys($this->fieldMap), function($properties) use ($path) { $query = "UPDATE " . $this->tableName . " SET "; $first = true; $values = []; foreach($properties as $key=>$value) { $dbField = $this->fieldMap[$key]['dbField']; if (!$first) { $query.= ', '; } $first = false; $query.=$dbField . ' = :' . $dbField; $values[$dbField] = $value; } $query.=" WHERE uri = :uri"; $values['uri'] = $path; $stmt = $this->pdo->prepare($query); $stmt->execute($values); return true; }); } /** * This method is used to search for principals matching a set of * properties. * * This search is specifically used by RFC3744's principal-property-search * REPORT. * * The actual search should be a unicode-non-case-sensitive search. The * keys in searchProperties are the WebDAV property names, while the values * are the property values to search on. * * By default, if multiple properties are submitted to this method, the * various properties should be combined with 'AND'. If $test is set to * 'anyof', it should be combined using 'OR'. * * This method should simply return an array with full principal uri's. * * If somebody attempted to search on a property the backend does not * support, you should simply return 0 results. * * You can also just return 0 results if you choose to not support * searching at all, but keep in mind that this may stop certain features * from working. * * @param string $prefixPath * @param array $searchProperties * @param string $test * @return array */ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE 1=1 '; $values = []; foreach($searchProperties as $property => $value) { switch($property) { case '{DAV:}displayname' : $query.=' AND displayname LIKE ?'; $values[] = '%' . $value . '%'; break; case '{http://sabredav.org/ns}email-address' : $query.=' AND email LIKE ?'; $values[] = '%' . $value . '%'; break; default : // Unsupported property return []; } } $stmt = $this->pdo->prepare($query); $stmt->execute($values); $principals = []; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { // Checking if the principal is in the prefix list($rowPrefix) = URLUtil::splitPath($row['uri']); if ($rowPrefix !== $prefixPath) continue; $principals[] = $row['uri']; } return $principals; } /** * Returns the list of members for a group-principal * * @param string $principal * @return array */ function getGroupMemberSet($principal) { $principal = $this->getPrincipalByPath($principal); if (!$principal) throw new DAV\Exception('Principal not found'); $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?'); $stmt->execute([$principal['id']]); $result = []; while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $result[] = $row['uri']; } return $result; } /** * Returns the list of groups a principal is a member of * * @param string $principal * @return array */ function getGroupMembership($principal) { $principal = $this->getPrincipalByPath($principal); if (!$principal) throw new DAV\Exception('Principal not found'); $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?'); $stmt->execute([$principal['id']]); $result = []; while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $result[] = $row['uri']; } return $result; } /** * Updates the list of group members for a group principal. * * The principals should be passed as a list of uri's. * * @param string $principal * @param array $members * @return void */ function setGroupMemberSet($principal, array $members) { // Grabbing the list of principal id's. $stmt = $this->pdo->prepare('SELECT id, uri FROM '.$this->tableName.' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');'); $stmt->execute(array_merge([$principal], $members)); $memberIds = []; $principalId = null; while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { if ($row['uri'] == $principal) { $principalId = $row['id']; } else { $memberIds[] = $row['id']; } } if (!$principalId) throw new DAV\Exception('Principal not found'); // Wiping out old members $stmt = $this->pdo->prepare('DELETE FROM '.$this->groupMembersTableName.' WHERE principal_id = ?;'); $stmt->execute([$principalId]); foreach($memberIds as $memberId) { $stmt = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);'); $stmt->execute([$principalId, $memberId]); } } } sabre-dav-2.1.10/lib/DAVACL/PrincipalCollection.php000066400000000000000000000015451267035675400216310ustar00rootroot00000000000000principalBackend, $principal); } } sabre-dav-2.1.10/lib/DAVACL/Property/000077500000000000000000000000001267035675400170025ustar00rootroot00000000000000sabre-dav-2.1.10/lib/DAVACL/Property/Acl.php000066400000000000000000000143731267035675400202220ustar00rootroot00000000000000privileges = $privileges; $this->prefixBaseUrl = $prefixBaseUrl; } /** * Returns the list of privileges for this property * * @return array */ function getPrivileges() { return $this->privileges; } /** * Serializes the property into a DOMElement * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server,\DOMElement $node) { $doc = $node->ownerDocument; foreach($this->privileges as $ace) { $this->serializeAce($doc, $node, $ace, $server); } } /** * Unserializes the {DAV:}acl xml element. * * @param \DOMElement $dom * @param array $propertyMap * @return Acl */ static function unserialize(\DOMElement $dom, array $propertyMap) { $privileges = []; $xaces = $dom->getElementsByTagNameNS('urn:DAV','ace'); for($ii=0; $ii < $xaces->length; $ii++) { $xace = $xaces->item($ii); $principal = $xace->getElementsByTagNameNS('urn:DAV','principal'); if ($principal->length !== 1) { throw new DAV\Exception\BadRequest('Each {DAV:}ace element must have one {DAV:}principal element'); } $principal = Principal::unserialize($principal->item(0), $propertyMap); switch($principal->getType()) { case Principal::HREF : $principal = $principal->getHref(); break; case Principal::AUTHENTICATED : $principal = '{DAV:}authenticated'; break; case Principal::UNAUTHENTICATED : $principal = '{DAV:}unauthenticated'; break; case Principal::ALL : $principal = '{DAV:}all'; break; } $protected = false; if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) { $protected = true; } $grants = $xace->getElementsByTagNameNS('urn:DAV','grant'); if ($grants->length < 1) { throw new DAV\Exception\NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); } $grant = $grants->item(0); $xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege'); for($jj=0; $jj<$xprivs->length; $jj++) { $xpriv = $xprivs->item($jj); $privilegeName = null; for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) { $childNode = $xpriv->childNodes->item($kk); if ($t = DAV\XMLUtil::toClarkNotation($childNode)) { $privilegeName = $t; break; } } if (is_null($privilegeName)) { throw new DAV\Exception\BadRequest('{DAV:}privilege elements must have a privilege element contained within them.'); } $privileges[] = [ 'principal' => $principal, 'protected' => $protected, 'privilege' => $privilegeName, ]; } } return new self($privileges); } /** * Serializes a single access control entry. * * @param \DOMDocument $doc * @param \DOMElement $node * @param array $ace * @param DAV\Server $server * @return void */ private function serializeAce($doc,$node,$ace, DAV\Server $server) { $xace = $doc->createElementNS('DAV:','d:ace'); $node->appendChild($xace); $principal = $doc->createElementNS('DAV:','d:principal'); $xace->appendChild($principal); switch($ace['principal']) { case '{DAV:}authenticated' : $principal->appendChild($doc->createElementNS('DAV:','d:authenticated')); break; case '{DAV:}unauthenticated' : $principal->appendChild($doc->createElementNS('DAV:','d:unauthenticated')); break; case '{DAV:}all' : $principal->appendChild($doc->createElementNS('DAV:','d:all')); break; default: $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/')); } $grant = $doc->createElementNS('DAV:','d:grant'); $xace->appendChild($grant); $privParts = null; preg_match('/^{([^}]*)}(.*)$/',$ace['privilege'],$privParts); $xprivilege = $doc->createElementNS('DAV:','d:privilege'); $grant->appendChild($xprivilege); $xprivilege->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); if (isset($ace['protected']) && $ace['protected']) $xace->appendChild($doc->createElement('d:protected')); } } sabre-dav-2.1.10/lib/DAVACL/Property/AclRestrictions.php000066400000000000000000000014361267035675400226270ustar00rootroot00000000000000ownerDocument; $elem->appendChild($doc->createElementNS('DAV:','d:grant-only')); $elem->appendChild($doc->createElementNS('DAV:','d:no-invert')); } } sabre-dav-2.1.10/lib/DAVACL/Property/CurrentUserPrivilegeSet.php000066400000000000000000000055701267035675400243260ustar00rootroot00000000000000privileges = $privileges; } /** * Serializes the property in the DOM * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server,\DOMElement $node) { $doc = $node->ownerDocument; foreach($this->privileges as $privName) { $this->serializePriv($doc,$node,$privName); } } /** * Returns true or false, whether the specified principal appears in the * list. * * @return bool */ function has($privilegeName) { return in_array($privilegeName, $this->privileges); } /** * Returns the list of privileges. * * @return array */ function getValue() { return $this->privileges; } /** * Serializes one privilege * * @param \DOMDocument $doc * @param \DOMElement $node * @param string $privName * @return void */ protected function serializePriv($doc,$node,$privName) { $xp = $doc->createElementNS('DAV:','d:privilege'); $node->appendChild($xp); $privParts = null; preg_match('/^{([^}]*)}(.*)$/',$privName,$privParts); $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); } /** * Unserializes the {DAV:}current-user-privilege-set element. * * @param \DOMElement $node * @param array $propertyMap * @return CurrentUserPrivilegeSet */ static function unserialize(\DOMElement $node, array $propertyMap) { $result = []; $xprivs = $node->getElementsByTagNameNS('urn:DAV','privilege'); for($jj=0; $jj<$xprivs->length; $jj++) { $xpriv = $xprivs->item($jj); $privilegeName = null; for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) { $childNode = $xpriv->childNodes->item($kk); if ($t = DAV\XMLUtil::toClarkNotation($childNode)) { $privilegeName = $t; break; } } $result[] = $privilegeName; } return new self($result); } } sabre-dav-2.1.10/lib/DAVACL/Property/Principal.php000066400000000000000000000076401267035675400214430ustar00rootroot00000000000000type = $type; if ($type===self::HREF && is_null($href)) { throw new DAV\Exception('The href argument must be specified for the HREF principal type.'); } $this->href = $href; } /** * Returns the principal type * * @return int */ function getType() { return $this->type; } /** * Returns the principal uri. * * @return string */ function getHref() { return $this->href; } /** * Serializes the property into a DOMElement. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server, \DOMElement $node) { $prefix = $server->xmlNamespaces['DAV:']; switch($this->type) { case self::UNAUTHENTICATED : $node->appendChild( $node->ownerDocument->createElement($prefix . ':unauthenticated') ); break; case self::AUTHENTICATED : $node->appendChild( $node->ownerDocument->createElement($prefix . ':authenticated') ); break; case self::HREF : $href = $node->ownerDocument->createElement($prefix . ':href'); $href->nodeValue = $server->getBaseUri() . DAV\URLUtil::encodePath($this->href); $node->appendChild($href); break; } } /** * Deserializes a DOM element into a property object. * * @param \DOMElement $dom * @param array $propertyMap * @return Principal */ static function unserialize(\DOMElement $dom, array $propertyMap) { $parent = $dom->firstChild; while(!DAV\XMLUtil::toClarkNotation($parent)) { $parent = $parent->nextSibling; } switch(DAV\XMLUtil::toClarkNotation($parent)) { case '{DAV:}unauthenticated' : return new self(self::UNAUTHENTICATED); case '{DAV:}authenticated' : return new self(self::AUTHENTICATED); case '{DAV:}href': return new self(self::HREF, $parent->textContent); case '{DAV:}all': return new self(self::ALL); default : throw new DAV\Exception\BadRequest('Unexpected element (' . DAV\XMLUtil::toClarkNotation($parent) . '). Could not deserialize'); } } } sabre-dav-2.1.10/lib/DAVACL/Property/SupportedPrivilegeSet.php000066400000000000000000000047551267035675400240360ustar00rootroot00000000000000privileges = $privileges; } /** * Returns the privilege value. * * @return array */ function getValue() { return $this->privileges; } /** * Serializes the property into a domdocument. * * @param DAV\Server $server * @param \DOMElement $node * @return void */ function serialize(DAV\Server $server,\DOMElement $node) { $doc = $node->ownerDocument; $this->serializePriv($doc, $node, $this->privileges); } /** * Serializes a property * * This is a recursive function. * * @param \DOMDocument $doc * @param \DOMElement $node * @param array $privilege * @return void */ private function serializePriv($doc,$node,$privilege) { $xsp = $doc->createElementNS('DAV:','d:supported-privilege'); $node->appendChild($xsp); $xp = $doc->createElementNS('DAV:','d:privilege'); $xsp->appendChild($xp); $privParts = null; preg_match('/^{([^}]*)}(.*)$/',$privilege['privilege'],$privParts); $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2])); if (isset($privilege['abstract']) && $privilege['abstract']) { $xsp->appendChild($doc->createElementNS('DAV:','d:abstract')); } if (isset($privilege['description'])) { $xsp->appendChild($doc->createElementNS('DAV:','d:description',$privilege['description'])); } if (isset($privilege['aggregates'])) { foreach($privilege['aggregates'] as $subPrivilege) { $this->serializePriv($doc,$xsp,$subPrivilege); } } } } sabre-dav-2.1.10/tests/000077500000000000000000000000001267035675400146205ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/000077500000000000000000000000001267035675400156545ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/000077500000000000000000000000001267035675400167065ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/000077500000000000000000000000001267035675400202355ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/AbstractPDOTest.php000066400000000000000000000752611267035675400237270ustar00rootroot00000000000000pdo); $this->assertTrue($backend instanceof PDO); } /** * @depends testConstruct */ function testGetCalendarsForUserNoCalendars() { $backend = new PDO($this->pdo); $calendars = $backend->getCalendarsForUser('principals/user2'); $this->assertEquals(array(),$calendars); } /** * @depends testConstruct */ function testCreateCalendarAndFetch() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array( '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT')), '{DAV:}displayname' => 'Hello!', '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp('transparent'), )); $calendars = $backend->getCalendarsForUser('principals/user2'); $elementCheck = array( 'id' => $returnedId, 'uri' => 'somerandomid', '{DAV:}displayname' => 'Hello!', '{urn:ietf:params:xml:ns:caldav}calendar-description' => '', '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp('transparent'), ); $this->assertInternalType('array',$calendars); $this->assertEquals(1,count($calendars)); foreach($elementCheck as $name=>$value) { $this->assertArrayHasKey($name, $calendars[0]); $this->assertEquals($value,$calendars[0][$name]); } } /** * @depends testConstruct */ function testUpdateCalendarAndFetch() { $backend = new PDO($this->pdo); //Creating a new calendar $newId = $backend->createCalendar('principals/user2','somerandomid',array()); $propPatch = new PropPatch([ '{DAV:}displayname' => 'myCalendar', '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp('transparent'), ]); // Updating the calendar $backend->updateCalendar($newId, $propPatch); $result = $propPatch->commit(); // Verifying the result of the update $this->assertTrue($result); // Fetching all calendars from this user $calendars = $backend->getCalendarsForUser('principals/user2'); // Checking if all the information is still correct $elementCheck = array( 'id' => $newId, 'uri' => 'somerandomid', '{DAV:}displayname' => 'myCalendar', '{urn:ietf:params:xml:ns:caldav}calendar-description' => '', '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => '', '{http://calendarserver.org/ns/}getctag' => 'http://sabre.io/ns/sync/2', '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp' => new CalDAV\Property\ScheduleCalendarTransp('transparent'), ); $this->assertInternalType('array',$calendars); $this->assertEquals(1,count($calendars)); foreach($elementCheck as $name=>$value) { $this->assertArrayHasKey($name, $calendars[0]); $this->assertEquals($value,$calendars[0][$name]); } } /** * @depends testUpdateCalendarAndFetch */ function testUpdateCalendarUnknownProperty() { $backend = new PDO($this->pdo); //Creating a new calendar $newId = $backend->createCalendar('principals/user2','somerandomid',array()); $propPatch = new PropPatch([ '{DAV:}displayname' => 'myCalendar', '{DAV:}yourmom' => 'wittycomment', ]); // Updating the calendar $backend->updateCalendar($newId, $propPatch); $propPatch->commit(); // Verifying the result of the update $this->assertEquals([ '{DAV:}yourmom' => 403, '{DAV:}displayname' => 424, ], $propPatch->getResult()); } /** * @depends testCreateCalendarAndFetch */ function testDeleteCalendar() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array( '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT')), '{DAV:}displayname' => 'Hello!', )); $backend->deleteCalendar($returnedId); $calendars = $backend->getCalendarsForUser('principals/user2'); $this->assertEquals(array(),$calendars); } /** * @depends testCreateCalendarAndFetch * @expectedException \Sabre\DAV\Exception */ function testCreateCalendarIncorrectComponentSet() {; $backend = new PDO($this->pdo); //Creating a new calendar $newId = $backend->createCalendar('principals/user2','somerandomid',array( '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => 'blabla', )); } function testCreateCalendarObject() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); $this->assertEquals(array( 'etag' => md5($object), 'size' => strlen($object), 'calendardata' => $object, 'firstoccurence' => strtotime('20120101'), 'lastoccurence' => strtotime('20120101')+(3600*24), 'componenttype' => 'VEVENT', ), $result->fetch(\PDO::FETCH_ASSOC)); } function testGetMultipleObjects() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'id-1', $object); $backend->createCalendarObject($returnedId, 'id-2', $object); $check = [ [ 'id' => 1, 'etag' => '"' . md5($object) . '"', 'uri' => 'id-1', 'size' => strlen($object), 'calendardata' => $object, 'lastmodified' => null, 'calendarid' => $returnedId, ], [ 'id' => 2, 'etag' => '"' . md5($object) . '"', 'uri' => 'id-2', 'size' => strlen($object), 'calendardata' => $object, 'lastmodified' => null, 'calendarid' => $returnedId, ], ]; $result = $backend->getMultipleCalendarObjects($returnedId, [ 'id-1', 'id-2' ]); foreach($check as $index => $props) { foreach($props as $key=>$value) { if ($key!=='lastmodified') { $this->assertEquals($value, $result[$index][$key]); } else { $this->assertTrue(isset($result[$index][$key])); } } } } /** * @expectedException Sabre\DAV\Exception\BadRequest * @depends testCreateCalendarObject */ function testCreateCalendarObjectNoComponent() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); } /** * @depends testCreateCalendarObject */ function testCreateCalendarObjectDuration() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nDURATION:P2D\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); $this->assertEquals(array( 'etag' => md5($object), 'size' => strlen($object), 'calendardata' => $object, 'firstoccurence' => strtotime('20120101'), 'lastoccurence' => strtotime('20120101')+(3600*48), 'componenttype' => 'VEVENT', ), $result->fetch(\PDO::FETCH_ASSOC)); } /** * @depends testCreateCalendarObject */ function testCreateCalendarObjectNoDTEND() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); $this->assertEquals(array( 'etag' => md5($object), 'size' => strlen($object), 'calendardata' => $object, 'firstoccurence' => strtotime('2012-01-01 10:00:00'), 'lastoccurence' => strtotime('2012-01-01 10:00:00'), 'componenttype' => 'VEVENT', ), $result->fetch(\PDO::FETCH_ASSOC)); } /** * @depends testCreateCalendarObject */ function testCreateCalendarObjectWithDTEND() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nDTEND:20120101T110000Z\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); $this->assertEquals(array( 'etag' => md5($object), 'size' => strlen($object), 'calendardata' => $object, 'firstoccurence' => strtotime('2012-01-01 10:00:00'), 'lastoccurence' => strtotime('2012-01-01 11:00:00'), 'componenttype' => 'VEVENT', ), $result->fetch(\PDO::FETCH_ASSOC)); } /** * @depends testCreateCalendarObject */ function testCreateCalendarObjectInfiniteReccurence() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nRRULE:FREQ=DAILY\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); $this->assertEquals(array( 'etag' => md5($object), 'size' => strlen($object), 'calendardata' => $object, 'firstoccurence' => strtotime('2012-01-01 10:00:00'), 'lastoccurence' => strtotime(PDO::MAX_DATE), 'componenttype' => 'VEVENT', ), $result->fetch(\PDO::FETCH_ASSOC)); } /** * @depends testCreateCalendarObject */ function testCreateCalendarObjectEndingReccurence() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE-TIME:20120101T100000Z\r\nDTEND;VALUE=DATE-TIME:20120101T110000Z\r\nUID:foo\r\nRRULE:FREQ=DAILY;COUNT=1000\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); $this->assertEquals(array( 'etag' => md5($object), 'size' => strlen($object), 'calendardata' => $object, 'firstoccurence' => strtotime('2012-01-01 10:00:00'), 'lastoccurence' => strtotime('2012-01-01 11:00:00') + (3600 * 24 * 999), 'componenttype' => 'VEVENT', ), $result->fetch(\PDO::FETCH_ASSOC)); } /** * @depends testCreateCalendarObject */ function testCreateCalendarObjectTask() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nDUE;VALUE=DATE-TIME:20120101T100000Z\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $result = $this->pdo->query('SELECT etag, size, calendardata, firstoccurence, lastoccurence, componenttype FROM calendarobjects WHERE uri = "random-id"'); $this->assertEquals(array( 'etag' => md5($object), 'size' => strlen($object), 'calendardata' => $object, 'firstoccurence' => null, 'lastoccurence' => null, 'componenttype' => 'VTODO', ), $result->fetch(\PDO::FETCH_ASSOC)); } /** * @depends testCreateCalendarObject */ function testGetCalendarObjects() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $data = $backend->getCalendarObjects($returnedId,'random-id'); $this->assertEquals(1, count($data)); $data = $data[0]; $this->assertEquals($returnedId, $data['calendarid']); $this->assertEquals('random-id', $data['uri']); $this->assertEquals(strlen($object),$data['size']); } /** * @depends testCreateCalendarObject */ function testGetCalendarObjectByUID() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',[]); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $this->assertNull( $backend->getCalendarObjectByUID('principals/user2', 'bar') ); $this->assertEquals( 'somerandomid/random-id', $backend->getCalendarObjectByUID('principals/user2', 'foo') ); } /** * @depends testCreateCalendarObject */ function testUpdateCalendarObject() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $object2 = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20130101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $backend->updateCalendarObject($returnedId, 'random-id', $object2); $data = $backend->getCalendarObject($returnedId,'random-id'); $this->assertEquals($object2, $data['calendardata']); $this->assertEquals($returnedId, $data['calendarid']); $this->assertEquals('random-id', $data['uri']); } /** * @depends testCreateCalendarObject */ function testDeleteCalendarObject() { $backend = new PDO($this->pdo); $returnedId = $backend->createCalendar('principals/user2','somerandomid',array()); $object = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($returnedId, 'random-id', $object); $backend->deleteCalendarObject($returnedId, 'random-id'); $data = $backend->getCalendarObject($returnedId,'random-id'); $this->assertNull($data); } function testCalendarQueryNoResult() { $abstract = new PDO($this->pdo); $filters = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VJOURNAL', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $this->assertEquals(array( ), $abstract->calendarQuery(1, $filters)); } function testCalendarQueryTodo() { $backend = new PDO($this->pdo); $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $filters = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VTODO', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $this->assertEquals(array( "todo", ), $backend->calendarQuery(1, $filters)); } function testCalendarQueryTodoNotMatch() { $backend = new PDO($this->pdo); $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $filters = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VTODO', 'comp-filters' => array(), 'prop-filters' => array( array( 'name' => 'summary', 'text-match' => null, 'time-range' => null, 'param-filters' => array(), 'is-not-defined' => false, ), ), 'is-not-defined' => false, 'time-range' => null, ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $this->assertEquals(array( ), $backend->calendarQuery(1, $filters)); } function testCalendarQueryNoFilter() { $backend = new PDO($this->pdo); $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $filters = array( 'name' => 'VCALENDAR', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $result = $backend->calendarQuery(1, $filters); $this->assertTrue(in_array('todo', $result)); $this->assertTrue(in_array('event', $result)); } function testCalendarQueryTimeRange() { $backend = new PDO($this->pdo); $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART;VALUE=DATE:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $filters = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('20120103'), 'end' => new \DateTime('20120104'), ), ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $this->assertEquals(array( "event2", ), $backend->calendarQuery(1, $filters)); } function testCalendarQueryTimeRangeNoEnd() { $backend = new PDO($this->pdo); $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $filters = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('20120102'), 'end' => null, ), ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $this->assertEquals(array( "event2", ), $backend->calendarQuery(1, $filters)); } function testGetChanges() { $backend = new PDO($this->pdo); $id = $backend->createCalendar( 'principals/user1', 'bla', [] ); $result = $backend->getChangesForCalendar($id, null, 1); $this->assertEquals([ 'syncToken' => 1, 'modified' => [], 'deleted' => [], 'added' => [], ], $result); $currentToken = $result['syncToken']; $dummyTodo = "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"; $backend->createCalendarObject($id, "todo1.ics", $dummyTodo); $backend->createCalendarObject($id, "todo2.ics", $dummyTodo); $backend->createCalendarObject($id, "todo3.ics", $dummyTodo); $backend->updateCalendarObject($id, "todo1.ics", $dummyTodo); $backend->deleteCalendarObject($id, "todo2.ics"); $result = $backend->getChangesForCalendar($id, $currentToken, 1); $this->assertEquals([ 'syncToken' => 6, 'modified' => ["todo1.ics"], 'deleted' => ["todo2.ics"], 'added' => ["todo3.ics"], ], $result); } function testCreateSubscriptions() { $props = [ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/cal.ics', false), '{DAV:}displayname' => 'cal', '{http://apple.com/ns/ical/}refreshrate' => 'P1W', '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF', '{http://calendarserver.org/ns/}subscribed-strip-todos' => true, //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true, '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true, ]; $backend = new PDO($this->pdo); $backend->createSubscription('principals/user1', 'sub1', $props); $subs = $backend->getSubscriptionsForUser('principals/user1'); $expected = $props; $expected['id'] = 1; $expected['uri'] = 'sub1'; $expected['principaluri'] = 'principals/user1'; unset($expected['{http://calendarserver.org/ns/}source']); $expected['source'] = 'http://example.org/cal.ics'; $this->assertEquals(1, count($subs)); foreach($expected as $k=>$v) { $this->assertEquals($subs[0][$k], $expected[$k]); } } /** * @expectedException \Sabre\DAV\Exception\Forbidden */ function testCreateSubscriptionFail() { $props = [ ]; $backend = new PDO($this->pdo); $backend->createSubscription('principals/user1', 'sub1', $props); } function testUpdateSubscriptions() { $props = [ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/cal.ics', false), '{DAV:}displayname' => 'cal', '{http://apple.com/ns/ical/}refreshrate' => 'P1W', '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF', '{http://calendarserver.org/ns/}subscribed-strip-todos' => true, //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true, '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true, ]; $backend = new PDO($this->pdo); $backend->createSubscription('principals/user1', 'sub1', $props); $newProps = [ '{DAV:}displayname' => 'new displayname', '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/cal2.ics', false), ]; $propPatch = new DAV\PropPatch($newProps); $backend->updateSubscription(1, $propPatch); $result = $propPatch->commit(); $this->assertTrue($result); $subs = $backend->getSubscriptionsForUser('principals/user1'); $expected = array_merge($props, $newProps); $expected['id'] = 1; $expected['uri'] = 'sub1'; $expected['principaluri'] = 'principals/user1'; unset($expected['{http://calendarserver.org/ns/}source']); $expected['source'] = 'http://example.org/cal2.ics'; $this->assertEquals(1, count($subs)); foreach($expected as $k=>$v) { $this->assertEquals($subs[0][$k], $expected[$k]); } } function testUpdateSubscriptionsFail() { $props = [ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/cal.ics', false), '{DAV:}displayname' => 'cal', '{http://apple.com/ns/ical/}refreshrate' => 'P1W', '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF', '{http://calendarserver.org/ns/}subscribed-strip-todos' => true, //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true, '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true, ]; $backend = new PDO($this->pdo); $backend->createSubscription('principals/user1', 'sub1', $props); $propPatch = new DAV\PropPatch([ '{DAV:}displayname' => 'new displayname', '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/cal2.ics', false), '{DAV:}unknown' => 'foo', ]); $backend->updateSubscription(1, $propPatch); $propPatch->commit(); $this->assertEquals([ '{DAV:}unknown' => 403, '{DAV:}displayname' => 424, '{http://calendarserver.org/ns/}source' => 424, ], $propPatch->getResult()); } function testDeleteSubscriptions() { $props = [ '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/cal.ics', false), '{DAV:}displayname' => 'cal', '{http://apple.com/ns/ical/}refreshrate' => 'P1W', '{http://apple.com/ns/ical/}calendar-color' => '#FF00FFFF', '{http://calendarserver.org/ns/}subscribed-strip-todos' => true, //'{http://calendarserver.org/ns/}subscribed-strip-alarms' => true, '{http://calendarserver.org/ns/}subscribed-strip-attachments' => true, ]; $backend = new PDO($this->pdo); $backend->createSubscription('principals/user1', 'sub1', $props); $newProps = [ '{DAV:}displayname' => 'new displayname', '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/cal2.ics', false), ]; $backend->deleteSubscription(1); $subs = $backend->getSubscriptionsForUser('principals/user1'); $this->assertEquals(0, count($subs)); } function testSchedulingMethods() { $backend = new PDO($this->pdo); $calData = "BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n"; $backend->createSchedulingObject( 'principals/user1', 'schedule1.ics', $calData ); $expected = [ 'calendardata' => $calData, 'uri' => 'schedule1.ics', 'etag' => '"' . md5($calData) . '"', 'size' => strlen($calData) ]; $result = $backend->getSchedulingObject('principals/user1', 'schedule1.ics'); foreach($expected as $k=>$v) { $this->assertArrayHasKey($k, $result); $this->assertEquals($v, $result[$k]); } $results = $backend->getSchedulingObjects('principals/user1'); $this->assertEquals(1, count($results)); $result = $results[0]; foreach($expected as $k=>$v) { $this->assertEquals($v, $result[$k]); } $backend->deleteSchedulingObject('principals/user1', 'schedule1.ics'); $result = $backend->getSchedulingObject('principals/user1', 'schedule1.ics'); $this->assertNull($result); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/AbstractTest.php000066400000000000000000000117321267035675400233550ustar00rootroot00000000000000 'anything'] ); $abstract->updateCalendar('randomid', $propPatch); $result = $propPatch->commit(); $this->assertFalse($result); } function testCalendarQuery() { $abstract = new AbstractMock(); $filters = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $this->assertEquals(array( 'event1.ics', ), $abstract->calendarQuery(1, $filters)); } function testGetCalendarObjectByUID() { $abstract = new AbstractMock(); $this->assertNull( $abstract->getCalendarObjectByUID('principal1', 'zim') ); $this->assertEquals( 'cal1/event1.ics', $abstract->getCalendarObjectByUID('principal1', 'foo') ); $this->assertNull( $abstract->getCalendarObjectByUID('principal3', 'foo') ); $this->assertNull( $abstract->getCalendarObjectByUID('principal1', 'shared') ); } function testGetMultipleCalendarObjects() { $abstract = new AbstractMock(); $result = $abstract->getMultipleCalendarObjects(1, [ 'event1.ics', 'task1.ics', ]); $expected = [ array( 'id' => 1, 'calendarid' => 1, 'uri' => 'event1.ics', 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", ), array( 'id' => 2, 'calendarid' => 1, 'uri' => 'task1.ics', 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", ), ]; $this->assertEquals($expected, $result); } } class AbstractMock extends AbstractBackend { function getCalendarsForUser($principalUri) { return array( array( 'id' => 1, 'principaluri' => 'principal1', 'uri' => 'cal1', ), array( 'id' => 2, 'principaluri' => 'principal1', '{http://sabredav.org/ns}owner-principal' => 'principal2', 'uri' => 'cal1', ), ); } function createCalendar($principalUri,$calendarUri,array $properties) { } function deleteCalendar($calendarId) { } function getCalendarObjects($calendarId) { switch($calendarId) { case 1: return [ [ 'id' => 1, 'calendarid' => 1, 'uri' => 'event1.ics', ], [ 'id' => 2, 'calendarid' => 1, 'uri' => 'task1.ics', ], ]; case 2: return [ [ 'id' => 3, 'calendarid' => 2, 'uri' => 'shared-event.ics', ] ]; } } function getCalendarObject($calendarId, $objectUri) { switch($objectUri) { case 'event1.ics' : return array( 'id' => 1, 'calendarid' => 1, 'uri' => 'event1.ics', 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", ); case 'task1.ics' : return array( 'id' => 2, 'calendarid' => 1, 'uri' => 'task1.ics', 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n", ); case 'shared-event.ics' : return array( 'id' => 3, 'calendarid' => 2, 'uri' => 'event1.ics', 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:shared\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", ); } } function createCalendarObject($calendarId,$objectUri,$calendarData) { } function updateCalendarObject($calendarId,$objectUri,$calendarData) { } function deleteCalendarObject($calendarId,$objectUri) { } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/Mock.php000066400000000000000000000141771267035675400216510ustar00rootroot00000000000000calendars = $calendars; $this->calendarData = $calendarData; } /** * 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 */ function getCalendarsForUser($principalUri) { $r = array(); foreach($this->calendars as $row) { if ($row['principaluri'] == $principalUri) { $r[] = $row; } } return $r; } /** * 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. * * This function must return a server-wide unique id that can be used * later to reference the calendar. * * @param string $principalUri * @param string $calendarUri * @param array $properties * @return string|int */ function createCalendar($principalUri,$calendarUri,array $properties) { $id = DAV\UUIDUtil::getUUID(); $this->calendars[] = array_merge(array( 'id' => $id, 'principaluri' => $principalUri, 'uri' => $calendarUri, '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')), ), $properties); return $id; } /** * Delete a calendar and all it's objects * * @param string $calendarId * @return void */ public function deleteCalendar($calendarId) { foreach($this->calendars as $k=>$calendar) { if ($calendar['id'] === $calendarId) { unset($this->calendars[$k]); } } } /** * Returns all calendar objects within a calendar object. * * 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. * * 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. * * @param string $calendarId * @return array */ public function getCalendarObjects($calendarId) { if (!isset($this->calendarData[$calendarId])) return array(); $objects = $this->calendarData[$calendarId]; foreach($objects as $uri => &$object) { $object['calendarid'] = $calendarId; $object['uri'] = $uri; $object['lastmodified'] = null; } return $objects; } /** * 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 */ function getCalendarObject($calendarId,$objectUri) { if (!isset($this->calendarData[$calendarId][$objectUri])) { throw new DAV\Exception\NotFound('Object could not be found'); } $object = $this->calendarData[$calendarId][$objectUri]; $object['calendarid'] = $calendarId; $object['uri'] = $objectUri; $object['lastmodified'] = null; return $object; } /** * Creates a new calendar object. * * @param string $calendarId * @param string $objectUri * @param string $calendarData * @return void */ function createCalendarObject($calendarId,$objectUri,$calendarData) { $this->calendarData[$calendarId][$objectUri] = array( 'calendardata' => $calendarData, 'calendarid' => $calendarId, 'uri' => $objectUri, ); return '"' . md5($calendarData) . '"'; } /** * Updates an existing calendarobject, based on it's uri. * * @param string $calendarId * @param string $objectUri * @param string $calendarData * @return void */ function updateCalendarObject($calendarId,$objectUri,$calendarData) { $this->calendarData[$calendarId][$objectUri] = array( 'calendardata' => $calendarData, 'calendarid' => $calendarId, 'uri' => $objectUri, ); return '"' . md5($calendarData) . '"'; } /** * Deletes an existing calendar object. * * @param string $calendarId * @param string $objectUri * @return void */ function deleteCalendarObject($calendarId,$objectUri) { throw new Exception('Not implemented'); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/MockScheduling.php000066400000000000000000000053321267035675400236500ustar00rootroot00000000000000schedulingObjects[$principalUri][$objectUri])) { return $this->schedulingObjects[$principalUri][$objectUri]; } } /** * Returns all scheduling objects for the inbox collection. * * These objects should be returned as an array. Every item in the array * should follow the same structure as returned from getSchedulingObject. * * The main difference is that 'calendardata' is optional. * * @param string $principalUri * @return array */ public function getSchedulingObjects($principalUri) { if (isset($this->schedulingObjects[$principalUri])) { return array_values($this->schedulingObjects[$principalUri]); } return []; } /** * Deletes a scheduling object * * @param string $principalUri * @param string $objectUri * @return void */ public function deleteSchedulingObject($principalUri, $objectUri) { if (isset($this->schedulingObjects[$principalUri][$objectUri])) { unset($this->schedulingObjects[$principalUri][$objectUri]); } } /** * Creates a new scheduling object. This should land in a users' inbox. * * @param string $principalUri * @param string $objectUri * @param string $objectData; * @return void */ public function createSchedulingObject($principalUri, $objectUri, $objectData) { if (!isset($this->schedulingObjects[$principalUri])) { $this->schedulingObjects[$principalUri] = []; } $this->schedulingObjects[$principalUri][$objectUri] = [ 'uri' => $objectUri, 'calendardata' => $objectData, 'lastmodified' => null, 'etag' => '"' . md5($objectData) . '"', 'size' => strlen($objectData) ]; } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/MockSharing.php000066400000000000000000000120261267035675400231540ustar00rootroot00000000000000notifications = $notifications; } /** * Returns a list of notifications for a given principal url. * * The returned array should only consist of implementations of * Sabre\CalDAV\Notifications\INotificationType. * * @param string $principalUri * @return array */ public function getNotificationsForPrincipal($principalUri) { if (isset($this->notifications[$principalUri])) { return $this->notifications[$principalUri]; } return array(); } /** * This deletes a specific notifcation. * * This may be called by a client once it deems a notification handled. * * @param string $principalUri * @param Sabre\CalDAV\Notifications\INotificationType $notification * @return void */ public function deleteNotification($principalUri, CalDAV\Notifications\INotificationType $notification) { foreach($this->notifications[$principalUri] as $key=>$value) { if ($notification === $value) { unset($this->notifications[$principalUri][$key]); } } } /** * Updates the list of shares. * * The first array is a list of people that are to be added to the * calendar. * * Every element in the add array has the following properties: * * href - A url. Usually a mailto: address * * commonName - Usually a first and last name, or false * * summary - A description of the share, can also be false * * readOnly - A boolean value * * Every element in the remove array is just the address string. * * Note that if the calendar is currently marked as 'not shared' by and * this method is called, the calendar should be 'upgraded' to a shared * calendar. * * @param mixed $calendarId * @param array $add * @param array $remove * @return void */ public function updateShares($calendarId, array $add, array $remove) { if (!isset($this->shares[$calendarId])) { $this->shares[$calendarId] = array(); } foreach($add as $val) { $val['status'] = CalDAV\SharingPlugin::STATUS_NORESPONSE; $this->shares[$calendarId][] = $val; } foreach($this->shares[$calendarId] as $k=>$share) { if (in_array($share['href'], $remove)) { unset($this->shares[$calendarId][$k]); } } // Re-numbering keys $this->shares[$calendarId] = array_values($this->shares[$calendarId]); } /** * Returns the list of people whom this calendar is shared with. * * Every element in this array should have the following properties: * * href - Often a mailto: address * * commonName - Optional, for example a first + last name * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. * * readOnly - boolean * * summary - Optional, a description for the share * * @param mixed $calendarId * @return array */ public function getShares($calendarId) { if (!isset($this->shares[$calendarId])) { return array(); } return $this->shares[$calendarId]; } /** * This method is called when a user replied to a request to share. * * @param string href The sharee who is replying (often a mailto: address) * @param int status One of the SharingPlugin::STATUS_* constants * @param string $calendarUri The url to the calendar thats being shared * @param string $inReplyTo The unique id this message is a response to * @param string $summary A description of the reply * @return void */ public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { // This operation basically doesn't do anything yet if ($status === CalDAV\SharingPlugin::STATUS_ACCEPTED) { return 'calendars/blabla/calendar'; } } /** * Publishes a calendar * * @param mixed $calendarId * @param bool $value * @return void */ public function setPublishStatus($calendarId, $value) { foreach($this->calendars as $k=>$cal) { if ($cal['id'] === $calendarId) { if (!$value) { unset($cal['{http://calendarserver.org/ns/}publish-url']); } else { $cal['{http://calendarserver.org/ns/}publish-url'] = 'http://example.org/public/ ' . $calendarId . '.ics'; } return; } } throw new DAV\Exception('Calendar with id "' . $calendarId . '" not found'); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/MockSubscriptionSupport.php000066400000000000000000000106671267035675400256530ustar00rootroot00000000000000subs[$principalUri])) { return $this->subs[$principalUri]; } return []; } /** * Creates a new subscription for a principal. * * If the creation was a success, an id must be returned that can be used to reference * this subscription in other methods, such as updateSubscription. * * @param string $principalUri * @param string $uri * @param array $properties * @return mixed */ public function createSubscription($principalUri, $uri, array $properties) { $properties['uri'] = $uri; $properties['principaluri'] = $principalUri; $properties['source'] = $properties['{http://calendarserver.org/ns/}source']->getHref(); if (!isset($this->subs[$principalUri])) { $this->subs[$principalUri] = []; } $id = [$principalUri, count($this->subs[$principalUri])+1]; $properties['id'] = $id; $this->subs[$principalUri][] = array_merge($properties, [ 'id' => $id, ]); return $id; } /** * Updates a subscription * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documenation for more info and examples. * * @param mixed $subscriptionId * @param \Sabre\DAV\PropPatch $propPatch * @return void */ public function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) { $found = null; foreach($this->subs[$subscriptionId[0]] as &$sub) { if ($sub['id'][1] === $subscriptionId[1]) { $found =& $sub; break; } } if (!$found) return; $propPatch->handleRemaining(function($mutations) use (&$found) { foreach($mutations as $k=>$v) { $found[$k] = $v; } return true; }); } /** * Deletes a subscription * * @param mixed $subscriptionId * @return void */ public function deleteSubscription($subscriptionId) { $found = null; foreach($this->subs[$subscriptionId[0]] as $index=>$sub) { if ($sub['id'][1] === $subscriptionId[1]) { unset($this->subs[$subscriptionId[0]][$index]); return true; } } return false; } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/PDOMySQLTest.php000066400000000000000000000017721267035675400231250ustar00rootroot00000000000000markTestSkipped('MySQL driver is not available, or not properly configured'); $pdo = \Sabre\TestUtil::getMySQLDB(); if (!$pdo) $this->markTestSkipped('Could not connect to mysql database'); $pdo->query('DROP TABLE IF EXISTS calendarobjects, calendars, calendarchanges, calendarsubscriptions, schedulingobjects'); $queries = explode( ';', file_get_contents(__DIR__ . '/../../../../examples/sql/mysql.calendars.sql') ); foreach($queries as $query) { $query = trim($query," \r\n\t"); if ($query) $pdo->exec($query); } $this->pdo = $pdo; } function teardown() { $this->pdo = null; } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Backend/PDOSqliteTest.php000066400000000000000000000016711267035675400234170ustar00rootroot00000000000000markTestSkipped('SQLite driver is not available'); if (file_exists(SABRE_TEMPDIR . '/testdb.sqlite')) unlink(SABRE_TEMPDIR . '/testdb.sqlite'); $pdo = new \PDO('sqlite:' . SABRE_TEMPDIR . '/testdb.sqlite'); $pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); // Yup this is definitely not 'fool proof', but good enough for now. $queries = explode(';', file_get_contents(__DIR__ . '/../../../../examples/sql/sqlite.calendars.sql')); foreach($queries as $query) { $pdo->exec($query); } $this->pdo = $pdo; } function teardown() { $this->pdo = null; unlink(SABRE_TEMPDIR . '/testdb.sqlite'); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarHomeNotificationsTest.php000066400000000000000000000024131267035675400253330ustar00rootroot00000000000000 'principals/user']); $this->assertEquals( [], $calendarHome->getChildren() ); } /** * @expectedException \Sabre\DAV\Exception\NotFound */ function testGetChildNoSupport() { $backend = new Backend\Mock(); $calendarHome = new CalendarHome($backend,['uri' => 'principals/user']); $calendarHome->getChild('notifications'); } function testGetChildren() { $backend = new Backend\MockSharing(); $calendarHome = new CalendarHome($backend,['uri' => 'principals/user']); $result = $calendarHome->getChildren(); $this->assertEquals('notifications', $result[0]->getName()); } function testGetChild() { $backend = new Backend\MockSharing(); $calendarHome = new CalendarHome($backend,['uri' => 'principals/user']); $result = $calendarHome->getChild('notifications'); $this->assertEquals('notifications', $result->getName()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarHomeSharedCalendarsTest.php000066400000000000000000000043131267035675400255460ustar00rootroot00000000000000 1, 'principaluri' => 'principals/user1', ), array( 'id' => 2, '{http://calendarserver.org/ns/}shared-url' => 'calendars/owner/cal1', '{http://sabredav.org/ns}owner-principal' => 'principal/owner', '{http://sabredav.org/ns}read-only' => false, 'principaluri' => 'principals/user1', ), ); $this->backend = new Backend\MockSharing( $calendars, array(), array() ); return new CalendarHome($this->backend, array( 'uri' => 'principals/user1' )); } function testSimple() { $instance = $this->getInstance(); $this->assertEquals('user1', $instance->getName()); } function testGetChildren() { $instance = $this->getInstance(); $children = $instance->getChildren(); $this->assertEquals(3, count($children)); // Testing if we got all the objects back. $hasShareable = false; $hasShared = false; $hasOutbox = false; $hasNotifications = false; foreach($children as $child) { if ($child instanceof IShareableCalendar) { $hasShareable = true; } if ($child instanceof ISharedCalendar) { $hasShared = true; } if ($child instanceof Notifications\ICollection) { $hasNotifications = true; } } if (!$hasShareable) $this->fail('Missing node!'); if (!$hasShared) $this->fail('Missing node!'); if (!$hasNotifications) $this->fail('Missing node!'); } function testShareReply() { $instance = $this->getInstance(); $result = $instance->shareReply('uri', SharingPlugin::STATUS_DECLINED, 'curi', '1'); $this->assertNull($result); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarHomeSubscriptionsTest.php000066400000000000000000000045201267035675400253720ustar00rootroot00000000000000 'baz', '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/test.ics'), ]; $principal = [ 'uri' => 'principals/user1' ]; $this->backend = new Backend\MockSubscriptionSupport([], []); $this->backend->createSubscription('principals/user1', 'uri', $props); return new CalendarHome($this->backend, $principal); } function testSimple() { $instance = $this->getInstance(); $this->assertEquals('user1', $instance->getName()); } function testGetChildren() { $instance = $this->getInstance(); $children = $instance->getChildren(); $this->assertEquals(1, count($children)); foreach($children as $child) { if ($child instanceof Subscriptions\Subscription) { return; } } $this->fail('There were no subscription nodes in the calendar home'); } function testCreateSubscription() { $instance = $this->getInstance(); $rt = ['{DAV:}collection', '{http://calendarserver.org/ns/}subscribed']; $props = [ '{DAV:}displayname' => 'baz', '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/test2.ics'), ]; $instance->createExtendedCollection('sub2', $rt, $props); $children = $instance->getChildren(); $this->assertEquals(2, count($children)); } /** * @expectedException \Sabre\DAV\Exception\InvalidResourceType */ public function testNoSubscriptionSupport() { $principal = [ 'uri' => 'principals/user1' ]; $backend = new Backend\Mock([], []); $uC = new CalendarHome($backend, $principal); $rt = ['{DAV:}collection', '{http://calendarserver.org/ns/}subscribed']; $props = [ '{DAV:}displayname' => 'baz', '{http://calendarserver.org/ns/}source' => new \Sabre\DAV\Property\Href('http://example.org/test2.ics'), ]; $uC->createExtendedCollection('sub2', $rt, $props); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarHomeTest.php000066400000000000000000000113301267035675400225770ustar00rootroot00000000000000markTestSkipped('SQLite driver is not available'); $this->backend = TestUtil::getBackend(); $this->usercalendars = new CalendarHome($this->backend, array( 'uri' => 'principals/user1' )); } function testSimple() { $this->assertEquals('user1',$this->usercalendars->getName()); } /** * @expectedException Sabre\DAV\Exception\NotFound * @depends testSimple */ function testGetChildNotFound() { $this->usercalendars->getChild('randomname'); } function testChildExists() { $this->assertFalse($this->usercalendars->childExists('foo')); $this->assertTrue($this->usercalendars->childExists('UUID-123467')); } function testGetOwner() { $this->assertEquals('principals/user1', $this->usercalendars->getOwner()); } function testGetGroup() { $this->assertNull($this->usercalendars->getGroup()); } function testGetACL() { $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ), ); $this->assertEquals($expected, $this->usercalendars->getACL()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetACL() { $this->usercalendars->setACL(array()); } /** * @expectedException Sabre\DAV\Exception\Forbidden * @depends testSimple */ function testSetName() { $this->usercalendars->setName('bla'); } /** * @expectedException Sabre\DAV\Exception\Forbidden * @depends testSimple */ function testDelete() { $this->usercalendars->delete(); } /** * @depends testSimple */ function testGetLastModified() { $this->assertNull($this->usercalendars->getLastModified()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed * @depends testSimple */ function testCreateFile() { $this->usercalendars->createFile('bla'); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed * @depends testSimple */ function testCreateDirectory() { $this->usercalendars->createDirectory('bla'); } /** * @depends testSimple */ function testCreateExtendedCollection() { $result = $this->usercalendars->createExtendedCollection('newcalendar', array('{DAV:}collection', '{urn:ietf:params:xml:ns:caldav}calendar'), array()); $this->assertNull($result); $cals = $this->backend->getCalendarsForUser('principals/user1'); $this->assertEquals(3,count($cals)); } /** * @expectedException Sabre\DAV\Exception\InvalidResourceType * @depends testSimple */ function testCreateExtendedCollectionBadResourceType() { $this->usercalendars->createExtendedCollection('newcalendar', array('{DAV:}collection','{DAV:}blabla'), array()); } /** * @expectedException Sabre\DAV\Exception\InvalidResourceType * @depends testSimple */ function testCreateExtendedCollectionNotACalendar() { $this->usercalendars->createExtendedCollection('newcalendar', array('{DAV:}collection'), array()); } function testGetSupportedPrivilegesSet() { $this->assertNull($this->usercalendars->getSupportedPrivilegeSet()); } /** * @expectedException Sabre\DAV\Exception\NotImplemented */ function testShareReplyFail() { $this->usercalendars->shareReply('uri', SharingPlugin::STATUS_DECLINED, 'curi', '1'); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarObjectTest.php000066400000000000000000000234321267035675400231230ustar00rootroot00000000000000markTestSkipped('SQLite driver is not available'); $this->backend = TestUtil::getBackend(); $calendars = $this->backend->getCalendarsForUser('principals/user1'); $this->assertEquals(2,count($calendars)); $this->calendar = new Calendar($this->backend, $calendars[0]); } function teardown() { unset($this->calendar); unset($this->backend); } function testSetup() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $this->assertInternalType('string',$children[0]->getName()); $this->assertInternalType('string',$children[0]->get()); $this->assertInternalType('string',$children[0]->getETag()); $this->assertEquals('text/calendar; charset=utf-8; component=vevent', $children[0]->getContentType()); } /** * @expectedException InvalidArgumentException */ function testInvalidArg1() { $obj = new CalendarObject( new Backend\Mock(array(),array()), array(), array() ); } /** * @expectedException InvalidArgumentException */ function testInvalidArg2() { $obj = new CalendarObject( new Backend\Mock(array(),array()), array(), array('calendarid' => '1') ); } /** * @depends testSetup */ function testPut() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $newData = TestUtil::getTestCalendarData(); $children[0]->put($newData); $this->assertEquals($newData, $children[0]->get()); } /** * @depends testSetup */ function testPutStream() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $newData = TestUtil::getTestCalendarData(); $stream = fopen('php://temp','r+'); fwrite($stream, $newData); rewind($stream); $children[0]->put($stream); $this->assertEquals($newData, $children[0]->get()); } /** * @depends testSetup */ function testDelete() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $obj = $children[0]; $obj->delete(); $children2 = $this->calendar->getChildren(); $this->assertEquals(count($children)-1, count($children2)); } /** * @depends testSetup */ function testGetLastModified() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $obj = $children[0]; $lastMod = $obj->getLastModified(); $this->assertTrue(is_int($lastMod) || ctype_digit($lastMod)); } /** * @depends testSetup */ function testGetSize() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $obj = $children[0]; $size = $obj->getSize(); $this->assertInternalType('int', $size); } function testGetOwner() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $obj = $children[0]; $this->assertEquals('principals/user1', $obj->getOwner()); } function testGetGroup() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $obj = $children[0]; $this->assertNull($obj->getGroup()); } function testGetACL() { $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), ); $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $obj = $children[0]; $this->assertEquals($expected, $obj->getACL()); } function testDefaultACL() { $backend = new Backend\Mock([], []); $calendarObject = new CalendarObject($backend, ['principaluri' => 'principals/user1'], ['calendarid' => 1, 'uri' => 'foo']); $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ), ); $this->assertEquals($expected, $calendarObject->getACL()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetACL() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $obj = $children[0]; $obj->setACL(array()); } function testGet() { $children = $this->calendar->getChildren(); $this->assertTrue($children[0] instanceof CalendarObject); $obj = $children[0]; $expected = "BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Apple Inc.//iCal 4.0.1//EN CALSCALE:GREGORIAN BEGIN:VTIMEZONE TZID:Asia/Seoul BEGIN:DAYLIGHT TZOFFSETFROM:+0900 RRULE:FREQ=YEARLY;UNTIL=19880507T150000Z;BYMONTH=5;BYDAY=2SU DTSTART:19870510T000000 TZNAME:GMT+09:00 TZOFFSETTO:+1000 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+1000 DTSTART:19881009T000000 TZNAME:GMT+09:00 TZOFFSETTO:+0900 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20100225T154229Z UID:39A6B5ED-DD51-4AFE-A683-C35EE3749627 TRANSP:TRANSPARENT SUMMARY:Something here DTSTAMP:20100228T130202Z DTSTART;TZID=Asia/Seoul:20100223T060000 DTEND;TZID=Asia/Seoul:20100223T070000 ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com SEQUENCE:2 END:VEVENT END:VCALENDAR"; $this->assertEquals($expected, $obj->get()); } function testGetRefetch() { $backend = new Backend\Mock(array(), array( 1 => array( 'foo' => array( 'calendardata' => 'foo', 'uri' => 'foo' ), ) )); $obj = new CalendarObject($backend, array('id' => 1), array('uri' => 'foo')); $this->assertEquals('foo', $obj->get()); } function testGetEtag1() { $objectInfo = array( 'calendardata' => 'foo', 'uri' => 'foo', 'etag' => 'bar', 'calendarid' => 1 ); $backend = new Backend\Mock(array(), array()); $obj = new CalendarObject($backend, array(), $objectInfo); $this->assertEquals('bar', $obj->getETag()); } function testGetEtag2() { $objectInfo = array( 'calendardata' => 'foo', 'uri' => 'foo', 'calendarid' => 1 ); $backend = new Backend\Mock(array(), array()); $obj = new CalendarObject($backend, array(), $objectInfo); $this->assertEquals('"' . md5('foo') . '"', $obj->getETag()); } function testGetSupportedPrivilegesSet() { $objectInfo = array( 'calendardata' => 'foo', 'uri' => 'foo', 'calendarid' => 1 ); $backend = new Backend\Mock(array(), array()); $obj = new CalendarObject($backend, array(), $objectInfo); $this->assertNull($obj->getSupportedPrivilegeSet()); } function testGetSize1() { $objectInfo = array( 'calendardata' => 'foo', 'uri' => 'foo', 'calendarid' => 1 ); $backend = new Backend\Mock(array(), array()); $obj = new CalendarObject($backend, array(), $objectInfo); $this->assertEquals(3, $obj->getSize()); } function testGetSize2() { $objectInfo = array( 'uri' => 'foo', 'calendarid' => 1, 'size' => 4, ); $backend = new Backend\Mock(array(), array()); $obj = new CalendarObject($backend, array(), $objectInfo); $this->assertEquals(4, $obj->getSize()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarQueryParserTest.php000066400000000000000000000400741267035675400242000ustar00rootroot00000000000000 ' . implode("\n", $xml) . ' '; $dom = DAV\XMLUtil::loadDOMDocument($xml); $q = new CalendarQueryParser($dom); $q->parse(); return $q->filters; } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testNoFilter() { $xml = array(); $this->parse($xml); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testTwoCompFilter() { $xml = array( '', ' ', ' ', '' ); $this->parse($xml); } function testBasicFilter() { $xml = array( '', ' ', '' ); $result = $this->parse($xml); $expected = array( 'name' => 'VCALENDAR', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => false ); $this->assertEquals( $expected, $result ); } function testCompIsNotDefined() { $xml = array( '', ' ', ' ', ' ', ' ', ' ', '' ); $result = $this->parse($xml); $expected = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => true, 'time-range' => false ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => false ); $this->assertEquals( $expected, $result ); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testCompTimeRangeOnVCALENDAR() { $xml = array( '', ' ', ' ', ' ', '' ); $result = $this->parse($xml); } function testCompTimeRange() { $xml = array( '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '' ); $result = $this->parse($xml); $expected = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2011-01-01 00:00:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-12-31 23:59:59', new \DateTimeZone('GMT')), ), ), array( 'name' => 'VTODO', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2011-01-01 00:00:00', new \DateTimeZone('GMT')), 'end' => null, ), ), array( 'name' => 'VJOURNAL', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => null, 'end' => new \DateTime('2011-12-31 23:59:59', new \DateTimeZone('GMT')), ), ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => false ); $this->assertEquals( $expected, $result ); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testCompTimeRangeBadRange() { $xml = array( '', ' ', ' ', ' ', ' ', ' ', '' ); $this->parse($xml); } function testProp() { $xml = array( '', ' ', ' ', ' ', ' vacation', ' ', ' ', ' ', '' ); $result = $this->parse($xml); $expected = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'is-not-defined' => false, 'comp-filters' => array(), 'prop-filters' => array( array( 'name' => 'SUMMARY', 'is-not-defined' => false, 'param-filters' => array(), 'text-match' => array( 'negate-condition' => false, 'collation' => 'i;ascii-casemap', 'value' => 'vacation', ), 'time-range' => null, ), ), 'time-range' => null, ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => false ); $this->assertEquals( $expected, $result ); } function testComplex() { $xml = array( '', ' ', ' ', ' ', ' vacation', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' DATE', ' ', ' ', ' ', ' ', '' ); $result = $this->parse($xml); $expected = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'is-not-defined' => false, 'comp-filters' => array(), 'prop-filters' => array( array( 'name' => 'SUMMARY', 'is-not-defined' => false, 'param-filters' => array(), 'text-match' => array( 'negate-condition' => false, 'collation' => 'i;unicode-casemap', 'value' => 'vacation', ), 'time-range' => null, ), array( 'name' => 'DTSTAMP', 'is-not-defined' => false, 'param-filters' => array(), 'text-match' => null, 'time-range' => array( 'start' => new \DateTime('2011-07-04 00:00:00', new \DateTimeZone('GMT')), 'end' => null, ), ), array( 'name' => 'ORGANIZER', 'is-not-defined' => true, 'param-filters' => array(), 'text-match' => null, 'time-range' => null, ), array( 'name' => 'DTSTART', 'is-not-defined' => false, 'param-filters' => array( array( 'name' => 'VALUE', 'is-not-defined' => false, 'text-match' => array( 'negate-condition' => true, 'value' => 'DATE', 'collation' => 'i;ascii-casemap', ), ), ), 'text-match' => null, 'time-range' => null, ), ), 'time-range' => null, ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => false ); $this->assertEquals( $expected, $result ); } function testOther1() { // This body was exactly sent to us from the sabredav mailing list. Checking if this parses correctly. $body = << BLA; $dom = DAV\XMLUtil::loadDOMDocument($body); $q = new CalendarQueryParser($dom); $q->parse(); $this->assertEquals(array( '{urn:ietf:params:xml:ns:caldav}calendar-data', '{DAV:}getetag', ), $q->requestedProperties); $expectedFilters = array( 'name' => 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'time-range' => array( 'start' => new \DateTime('2009-01-01 00:00:00', new \DateTimeZone('UTC')), 'end' => new \DateTime('2012-12-02 00:00:00', new \DateTimeZone('UTC')), ), 'is-not-defined' => false, ), ), 'prop-filters' => array(), 'time-range' => null, 'is-not-defined' => false, ); $this->assertEquals($expectedFilters, $q->filters); } function testExpand() { $xml = array( '', ' ', ' ', ' ', '', '', ' ', '' ); $xml = ' ' . implode("\n", $xml) . ' '; $dom = DAV\XMLUtil::loadDOMDocument($xml); $q = new CalendarQueryParser($dom); $q->parse(); $expected = array( 'name' => 'VCALENDAR', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => false ); $this->assertEquals( $expected, $q->filters ); $this->assertEquals(array( '{urn:ietf:params:xml:ns:caldav}calendar-data', ), $q->requestedProperties); $this->assertEquals( array( 'start' => new \DateTime('2011-01-01 00:00:00', new \DateTimeZone('UTC')), 'end' => new \DateTime('2012-01-01 00:00:00', new \DateTimeZone('UTC')), ), $q->expand ); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testExpandNoStart() { $xml = array( '', ' ', ' ', ' ', '', '', ' ', '' ); $xml = ' ' . implode("\n", $xml) . ' '; $dom = DAV\XMLUtil::loadDOMDocument($xml); $q = new CalendarQueryParser($dom); $q->parse(); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testExpandNoEnd() { $xml = array( '', ' ', ' ', ' ', '', '', ' ', '' ); $xml = ' ' . implode("\n", $xml) . ' '; $dom = DAV\XMLUtil::loadDOMDocument($xml); $q = new CalendarQueryParser($dom); $q->parse(); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testExpandBadTimes() { $xml = array( '', ' ', ' ', ' ', '', '', ' ', '' ); $xml = ' ' . implode("\n", $xml) . ' '; $dom = DAV\XMLUtil::loadDOMDocument($xml); $q = new CalendarQueryParser($dom); $q->parse(); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarQueryVAlarmTest.php000066400000000000000000000074611267035675400241310ustar00rootroot00000000000000createComponent('VEVENT'); $vevent->RRULE = 'FREQ=MONTHLY'; $vevent->DTSTART = '20120101T120000Z'; $vevent->UID = 'bla'; $valarm = $vcalendar->createComponent('VALARM'); $valarm->TRIGGER = '-P15D'; $vevent->add($valarm); $vcalendar->add($vevent); $filter = array( 'name' => 'VCALENDAR', 'is-not-defined' => false, 'time-range' => null, 'prop-filters' => array(), 'comp-filters' => array( array( 'name' => 'VEVENT', 'is-not-defined' => false, 'time-range' => null, 'prop-filters' => array(), 'comp-filters' => array( array( 'name' => 'VALARM', 'is-not-defined' => false, 'prop-filters' => array(), 'comp-filters' => array(), 'time-range' => array( 'start' => new \DateTime('2012-05-10'), 'end' => new \DateTime('2012-05-20'), ), ), ), ), ), ); $validator = new CalendarQueryValidator(); $this->assertTrue($validator->validate($vcalendar, $filter)); $vcalendar = new VObject\Component\VCalendar(); // A limited recurrence rule, should return false $vevent = $vcalendar->createComponent('VEVENT'); $vevent->RRULE = 'FREQ=MONTHLY;COUNT=1'; $vevent->DTSTART = '20120101T120000Z'; $vevent->UID = 'bla'; $valarm = $vcalendar->createComponent('VALARM'); $valarm->TRIGGER = '-P15D'; $vevent->add($valarm); $vcalendar->add($vevent); $this->assertFalse($validator->validate($vcalendar, $filter)); } function testAlarmWayBefore() { $vcalendar = new VObject\Component\VCalendar(); $vevent = $vcalendar->createComponent('VEVENT'); $vevent->DTSTART = '20120101T120000Z'; $vevent->UID = 'bla'; $valarm = $vcalendar->createComponent('VALARM'); $valarm->TRIGGER = '-P2W1D'; $vevent->add($valarm); $vcalendar->add($vevent); $filter = array( 'name' => 'VCALENDAR', 'is-not-defined' => false, 'time-range' => null, 'prop-filters' => array(), 'comp-filters' => array( array( 'name' => 'VEVENT', 'is-not-defined' => false, 'time-range' => null, 'prop-filters' => array(), 'comp-filters' => array( array( 'name' => 'VALARM', 'is-not-defined' => false, 'prop-filters' => array(), 'comp-filters' => array(), 'time-range' => array( 'start' => new \DateTime('2011-12-10'), 'end' => new \DateTime('2011-12-20'), ), ), ), ), ), ); $validator = new CalendarQueryValidator(); $this->assertTrue($validator->validate($vcalendar, $filter)); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarQueryValidatorTest.php000066400000000000000000000526641267035675400247010ustar00rootroot00000000000000assertFalse($validator->validate($vcal, ['name' => 'VFOO'])); } /** * @dataProvider provider */ function testValid($icalObject, $filters, $outcome) { $validator = new CalendarQueryValidator(); // Wrapping filter in a VCALENDAR component filter, as this is always // there anyway. $filters = array( 'name' => 'VCALENDAR', 'comp-filters' => array($filters), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $vObject = VObject\Reader::read($icalObject); switch($outcome) { case 0 : $this->assertFalse($validator->validate($vObject, $filters)); break; case 1 : $this->assertTrue($validator->validate($vObject, $filters)); break; case -1 : try { $validator->validate($vObject, $filters); $this->fail('This test was supposed to fail'); } catch (\Exception $e) { // We need to test something to be valid for phpunit strict // mode. $this->assertTrue(true); } catch (\Throwable $e) { // PHP7 $this->assertTrue(true); } break; } } function provider() { $blob1 = << 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $filter2 = $filter1; $filter2['name'] = 'VTODO'; $filter3 = $filter1; $filter3['is-not-defined'] = true; $filter4 = $filter1; $filter4['name'] = 'VTODO'; $filter4['is-not-defined'] = true; $filter5 = $filter1; $filter5['comp-filters'] = array( array( 'name' => 'VALARM', 'is-not-defined' => false, 'comp-filters' => array(), 'prop-filters' => array(), 'time-range' => null, ), ); $filter6 = $filter1; $filter6['prop-filters'] = array( array( 'name' => 'SUMMARY', 'is-not-defined' => false, 'param-filters' => array(), 'time-range' => null, 'text-match' => null, ), ); $filter7 = $filter6; $filter7['prop-filters'][0]['name'] = 'DESCRIPTION'; $filter8 = $filter6; $filter8['prop-filters'][0]['is-not-defined'] = true; $filter9 = $filter7; $filter9['prop-filters'][0]['is-not-defined'] = true; $filter10 = $filter5; $filter10['prop-filters'] = $filter6['prop-filters']; // Param filters $filter11 = $filter1; $filter11['prop-filters'] = array( array( 'name' => 'DTSTART', 'is-not-defined' => false, 'param-filters' => array( array( 'name' => 'VALUE', 'is-not-defined' => false, 'text-match' => null, ), ), 'time-range' => null, 'text-match' => null, ), ); $filter12 = $filter11; $filter12['prop-filters'][0]['param-filters'][0]['name'] = 'TZID'; $filter13 = $filter11; $filter13['prop-filters'][0]['param-filters'][0]['is-not-defined'] = true; $filter14 = $filter12; $filter14['prop-filters'][0]['param-filters'][0]['is-not-defined'] = true; // Param text filter $filter15 = $filter11; $filter15['prop-filters'][0]['param-filters'][0]['text-match'] = array( 'collation' => 'i;ascii-casemap', 'value' => 'dAtE', 'negate-condition' => false, ); $filter16 = $filter15; $filter16['prop-filters'][0]['param-filters'][0]['text-match']['collation'] = 'i;octet'; $filter17 = $filter15; $filter17['prop-filters'][0]['param-filters'][0]['text-match']['negate-condition'] = true; $filter18 = $filter15; $filter18['prop-filters'][0]['param-filters'][0]['text-match']['negate-condition'] = true; $filter18['prop-filters'][0]['param-filters'][0]['text-match']['collation'] = 'i;octet'; // prop + text $filter19 = $filter5; $filter19['comp-filters'][0]['prop-filters'] = array( array( 'name' => 'action', 'is-not-defined' => false, 'time-range' => null, 'param-filters' => array(), 'text-match' => array( 'collation' => 'i;ascii-casemap', 'value' => 'display', 'negate-condition' => false, ), ), ); // Time range $filter20 = array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2011-01-01 10:00:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 13:00:00', new \DateTimeZone('GMT')), ), ); // Time range, no end date $filter21 = $filter20; $filter21['time-range']['end'] = null; // Time range, no start date $filter22 = $filter20; $filter22['time-range']['start'] = null; // Time range, other dates $filter23 = $filter20; $filter23['time-range'] = array( 'start' => new \DateTime('2011-02-01 10:00:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-02-01 13:00:00', new \DateTimeZone('GMT')), ); // Time range $filter24 = array( 'name' => 'VTODO', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2011-01-01 12:45:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 13:15:00', new \DateTimeZone('GMT')), ), ); // Time range, other dates (1 month in the future) $filter25 = $filter24; $filter25['time-range'] = array( 'start' => new \DateTime('2011-02-01 10:00:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-02-01 13:00:00', new \DateTimeZone('GMT')), ); $filter26 = $filter24; $filter26['time-range'] = array( 'start' => new \DateTime('2011-01-01 11:45:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 12:15:00', new \DateTimeZone('GMT')), ); // Time range for VJOURNAL $filter27 = array( 'name' => 'VJOURNAL', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2011-01-01 12:45:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 13:15:00', new \DateTimeZone('GMT')), ), ); $filter28 = $filter27; $filter28['time-range'] = array( 'start' => new \DateTime('2011-01-01 11:45:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 12:15:00', new \DateTimeZone('GMT')), ); // Time range for VFREEBUSY $filter29 = array( 'name' => 'VFREEBUSY', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2011-01-01 12:45:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 13:15:00', new \DateTimeZone('GMT')), ), ); // Time range filter on property $filter30 = array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array( array( 'name' => 'DTSTART', 'is-not-defined' => false, 'param-filters' => array(), 'time-range' => array( 'start' => new \DateTime('2011-01-01 10:00:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 13:00:00', new \DateTimeZone('GMT')), ), 'text-match' => null, ), ), 'is-not-defined' => false, 'time-range' => null, ); // Time range for alarm $filter31 = array( 'name' => 'VEVENT', 'prop-filters' => array(), 'comp-filters' => array( array( 'name' => 'VALARM', 'is-not-defined' => false, 'comp-filters' => array(), 'prop-filters' => array(), 'time-range' => array( 'start' => new \DateTime('2011-01-01 10:45:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 11:15:00', new \DateTimeZone('GMT')), ), 'text-match' => null, ), ), 'is-not-defined' => false, 'time-range' => null, ); $filter32 = $filter31; $filter32['comp-filters'][0]['time-range'] = array( 'start' => new \DateTime('2011-01-01 11:45:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 12:15:00', new \DateTimeZone('GMT')), ); $filter33 = $filter31; $filter33['name'] = 'VTODO'; $filter34 = $filter32; $filter34['name'] = 'VTODO'; $filter35 = $filter31; $filter35['name'] = 'VJOURNAL'; $filter36 = $filter32; $filter36['name'] = 'VJOURNAL'; // Time range filter on non-datetime property $filter37 = array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array( array( 'name' => 'SUMMARY', 'is-not-defined' => false, 'param-filters' => array(), 'time-range' => array( 'start' => new \DateTime('2011-01-01 10:00:00', new \DateTimeZone('GMT')), 'end' => new \DateTime('2011-01-01 13:00:00', new \DateTimeZone('GMT')), ), 'text-match' => null, ), ), 'is-not-defined' => false, 'time-range' => null, ); $filter38 = array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2012-07-01 00:00:00', new \DateTimeZone('UTC')), 'end' => new \DateTime('2012-08-01 00:00:00', new \DateTimeZone('UTC')), ) ); $filter39 = array( 'name' => 'VEVENT', 'comp-filters' => array( array( 'name' => 'VALARM', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2012-09-01 00:00:00', new \DateTimeZone('UTC')), 'end' => new \DateTime('2012-10-01 00:00:00', new \DateTimeZone('UTC')), ) ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); return array( // Component check array($blob1, $filter1, 1), array($blob1, $filter2, 0), array($blob1, $filter3, 0), array($blob1, $filter4, 1), // Subcomponent check (4) array($blob1, $filter5, 0), array($blob2, $filter5, 1), // Property checki (6) array($blob1, $filter6, 1), array($blob1, $filter7, 0), array($blob1, $filter8, 0), array($blob1, $filter9, 1), // Subcomponent + property (10) array($blob2, $filter10, 1), // Param filter (11) array($blob3, $filter11, 1), array($blob3, $filter12, 0), array($blob3, $filter13, 0), array($blob3, $filter14, 1), // Param + text (15) array($blob3, $filter15, 1), array($blob3, $filter16, 0), array($blob3, $filter17, 0), array($blob3, $filter18, 1), // Prop + text (19) array($blob2, $filter19, 1), // Incorrect object (vcard) (20) array($blob4, $filter1, -1), // Time-range for event (21) array($blob5, $filter20, 1), array($blob6, $filter20, 1), array($blob7, $filter20, 1), array($blob8, $filter20, 1), array($blob5, $filter21, 1), array($blob5, $filter22, 1), array($blob5, $filter23, 0), array($blob6, $filter23, 0), array($blob7, $filter23, 0), array($blob8, $filter23, 0), // Time-range for todo (31) array($blob9, $filter24, 1), array($blob9, $filter25, 0), array($blob9, $filter26, 1), array($blob10, $filter24, 1), array($blob10, $filter25, 0), array($blob10, $filter26, 1), array($blob11, $filter24, 0), array($blob11, $filter25, 0), array($blob11, $filter26, 1), array($blob12, $filter24, 1), array($blob12, $filter25, 0), array($blob12, $filter26, 0), array($blob13, $filter24, 1), array($blob13, $filter25, 0), array($blob13, $filter26, 1), array($blob14, $filter24, 1), array($blob14, $filter25, 0), array($blob14, $filter26, 0), array($blob15, $filter24, 1), array($blob15, $filter25, 1), array($blob15, $filter26, 1), array($blob16, $filter24, 1), array($blob16, $filter25, 1), array($blob16, $filter26, 1), // Time-range for journals (55) array($blob17, $filter27, 0), array($blob17, $filter28, 0), array($blob18, $filter27, 0), array($blob18, $filter28, 1), array($blob19, $filter27, 1), array($blob19, $filter28, 1), // Time-range for free-busy (61) array($blob20, $filter29, -1), // Time-range on property (62) array($blob5, $filter30, 1), array($blob3, $filter37, -1), array($blob3, $filter30, 0), // Time-range on alarm in vevent (65) array($blob21, $filter31, 1), array($blob21, $filter32, 0), array($blob22, $filter31, 1), array($blob22, $filter32, 0), array($blob23, $filter31, 1), array($blob23, $filter32, 0), array($blob24, $filter31, 1), array($blob24, $filter32, 0), array($blob25, $filter31, 1), array($blob25, $filter32, 0), array($blob26, $filter31, 1), array($blob26, $filter32, 0), // Time-range on alarm for vtodo (77) array($blob27, $filter33, 1), array($blob27, $filter34, 0), // Time-range on alarm for vjournal (79) array($blob28, $filter35, -1), array($blob28, $filter36, -1), // Time-range on alarm with duration (81) array($blob29, $filter31, 1), array($blob29, $filter32, 0), array($blob30, $filter31, 0), array($blob30, $filter32, 0), // Time-range with RRULE (85) array($blob31, $filter20, 1), array($blob32, $filter20, 0), // Bug reported on mailing list, related to all-day events (87) //array($blob33, $filter38, 1), // Event in timerange, but filtered alarm is in the far future (88). array($blob34, $filter39, 0), ); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/CalendarTest.php000066400000000000000000000153761267035675400220040ustar00rootroot00000000000000markTestSkipped('SQLite driver is not available'); $this->backend = TestUtil::getBackend(); $this->calendars = $this->backend->getCalendarsForUser('principals/user1'); $this->assertEquals(2, count($this->calendars)); $this->calendar = new Calendar($this->backend, $this->calendars[0]); } function teardown() { unset($this->backend); } function testSimple() { $this->assertEquals($this->calendars[0]['uri'], $this->calendar->getName()); } /** * @depends testSimple */ function testUpdateProperties() { $propPatch = new PropPatch([ '{DAV:}displayname' => 'NewName', ]); $result = $this->calendar->propPatch($propPatch); $result = $propPatch->commit(); $this->assertEquals(true, $result); $calendars2 = $this->backend->getCalendarsForUser('principals/user1'); $this->assertEquals('NewName',$calendars2[0]['{DAV:}displayname']); } /** * @depends testSimple */ function testGetProperties() { $question = array( '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set', ); $result = $this->calendar->getProperties($question); foreach($question as $q) $this->assertArrayHasKey($q,$result); $this->assertEquals(array('VEVENT','VTODO'), $result['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue()); } /** * @expectedException Sabre\DAV\Exception\NotFound * @depends testSimple */ function testGetChildNotFound() { $this->calendar->getChild('randomname'); } /** * @depends testSimple */ function testGetChildren() { $children = $this->calendar->getChildren(); $this->assertEquals(1,count($children)); $this->assertTrue($children[0] instanceof CalendarObject); } /** * @depends testGetChildren */ function testChildExists() { $this->assertFalse($this->calendar->childExists('foo')); $children = $this->calendar->getChildren(); $this->assertTrue($this->calendar->childExists($children[0]->getName())); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testCreateDirectory() { $this->calendar->createDirectory('hello'); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetName() { $this->calendar->setName('hello'); } function testGetLastModified() { $this->assertNull($this->calendar->getLastModified()); } function testCreateFile() { $file = fopen('php://memory','r+'); fwrite($file,TestUtil::getTestCalendarData()); rewind($file); $this->calendar->createFile('hello',$file); $file = $this->calendar->getChild('hello'); $this->assertTrue($file instanceof CalendarObject); } function testCreateFileNoSupportedComponents() { $file = fopen('php://memory','r+'); fwrite($file,TestUtil::getTestCalendarData()); rewind($file); $calendar = new Calendar($this->backend, $this->calendars[1]); $calendar->createFile('hello',$file); $file = $calendar->getChild('hello'); $this->assertTrue($file instanceof CalendarObject); } function testDelete() { $this->calendar->delete(); $calendars = $this->backend->getCalendarsForUser('principals/user1'); $this->assertEquals(1, count($calendars)); } function testGetOwner() { $this->assertEquals('principals/user1',$this->calendar->getOwner()); } function testGetGroup() { $this->assertNull($this->calendar->getGroup()); } function testGetACL() { $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ), array( 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', 'principal' => '{DAV:}authenticated', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), ); $this->assertEquals($expected, $this->calendar->getACL()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetACL() { $this->calendar->setACL(array()); } function testGetSupportedPrivilegesSet() { $result = $this->calendar->getSupportedPrivilegeSet(); $this->assertEquals( '{' . Plugin::NS_CALDAV . '}read-free-busy', $result['aggregates'][0]['aggregates'][2]['privilege'] ); } function testGetSyncToken() { $this->assertEquals(2, $this->calendar->getSyncToken()); } function testGetSyncToken2() { $calendar = new Calendar(new Backend\Mock([],[]), [ '{DAV:}sync-token' => 2 ]); $this->assertEquals(2, $this->calendar->getSyncToken()); } function testGetSyncTokenNoSyncSupport() { $calendar = new Calendar(new Backend\Mock([],[]), []); $this->assertNull($calendar->getSyncToken()); } function testGetChanges() { $this->assertEquals([ 'syncToken' => 2, 'modified' => [], 'deleted' => [], 'added' => ['UUID-2345'], ], $this->calendar->getChanges(1, 1)); } function testGetChangesNoSyncSupport() { $calendar = new Calendar(new Backend\Mock([],[]), []); $this->assertNull($calendar->getChanges(1,null)); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDTest.php000066400000000000000000000065771267035675400252120ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:foobar DTEND;TZID=Europe/Berlin:20120207T191500 RRULE:FREQ=DAILY;INTERVAL=1;COUNT=3 SUMMARY:RecurringEvents 3 times DTSTART;TZID=Europe/Berlin:20120207T181500 END:VEVENT BEGIN:VEVENT CREATED:20120207T111900Z UID:foobar DTEND;TZID=Europe/Berlin:20120208T191500 SUMMARY:RecurringEvents 3 times OVERWRITTEN DTSTART;TZID=Europe/Berlin:20120208T181500 RECURRENCE-ID;TZID=Europe/Berlin:20120208T181500 END:VEVENT END:VCALENDAR ', ), ), ); function testExpand() { $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/calendars/user1/calendar1', 'HTTP_DEPTH' => '1', ]); $request->setBody(' '); $response = $this->request($request); // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); // check if DTSTARTs and DTENDs are correct foreach ($vObject->VEVENT as $vevent) { /** @var $vevent Sabre\VObject\Component\VEvent */ foreach ($vevent->children as $child) { /** @var $child Sabre\VObject\Property */ if ($child->name == 'DTSTART') { // DTSTART has to be one of three valid values $this->assertContains($child->getValue(), ['20120207T171500Z', '20120208T171500Z', '20120209T171500Z'], 'DTSTART is not a valid value: '.$child->getValue()); } elseif ($child->name == 'DTEND') { // DTEND has to be one of three valid values $this->assertContains($child->getValue(), ['20120207T181500Z', '20120208T181500Z', '20120209T181500Z'], 'DTEND is not a valid value: '.$child->getValue()); } } } } } sabre-dav-2.1.10/tests/Sabre/CalDAV/ExpandEventsDTSTARTandDTENDbyDayTest.php000066400000000000000000000063311267035675400261670ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:foobar DTEND;TZID=Europe/Berlin:20120207T191500 RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,TH SUMMARY:RecurringEvents on tuesday and thursday DTSTART;TZID=Europe/Berlin:20120207T181500 END:VEVENT END:VCALENDAR ', ), ), ); function testExpandRecurringByDayEvent() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/calendars/user1/calendar1', 'HTTP_DEPTH' => '1', )); $request->setBody(' '); $response = $this->request($request); // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); $this->assertEquals(2, count($vObject->VEVENT)); // check if DTSTARTs and DTENDs are correct foreach ($vObject->VEVENT as $vevent) { /** @var $vevent Sabre\VObject\Component\VEvent */ foreach ($vevent->children as $child) { /** @var $child Sabre\VObject\Property */ if ($child->name == 'DTSTART') { // DTSTART has to be one of two valid values $this->assertContains($child->getValue(), array('20120214T171500Z', '20120216T171500Z'), 'DTSTART is not a valid value: '.$child->getValue()); } elseif ($child->name == 'DTEND') { // DTEND has to be one of two valid values $this->assertContains($child->getValue(), array('20120214T181500Z', '20120216T181500Z'), 'DTEND is not a valid value: '.$child->getValue()); } } } } } sabre-dav-2.1.10/tests/Sabre/CalDAV/ExpandEventsDoubleEventsTest.php000066400000000000000000000054731267035675400252140ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:foobar DTEND;TZID=Europe/Berlin:20120207T191500 RRULE:FREQ=DAILY;INTERVAL=1;COUNT=3 SUMMARY:RecurringEvents 3 times DTSTART;TZID=Europe/Berlin:20120207T181500 END:VEVENT BEGIN:VEVENT CREATED:20120207T111900Z UID:foobar DTEND;TZID=Europe/Berlin:20120208T191500 SUMMARY:RecurringEvents 3 times OVERWRITTEN DTSTART;TZID=Europe/Berlin:20120208T181500 RECURRENCE-ID;TZID=Europe/Berlin:20120208T181500 END:VEVENT END:VCALENDAR ', ), ), ); function testExpand() { $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/calendars/user1/calendar1', 'HTTP_DEPTH' => '1', ]); $request->setBody(' '); $response = $this->request($request); // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); // We only expect 3 events $this->assertEquals(3, count($vObject->VEVENT),'We got 6 events instead of 3. Output: ' . $body); // TZID should be gone $this->assertFalse(isset($vObject->VEVENT->DTSTART['TZID'])); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/ExpandEventsFloatingTimeTest.php000066400000000000000000000150101267035675400251630ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU DTSTART:19810329T020000 TZNAME:GMT+2 TZOFFSETTO:+0200 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU DTSTART:19961027T030000 TZNAME:GMT+1 TZOFFSETTO:+0100 END:STANDARD END:VTIMEZONE END:VCALENDAR', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN BEGIN:VEVENT CREATED:20140701T143658Z UID:dba46fe8-1631-4d98-a575-97963c364dfe DTEND:20141108T073000 TRANSP:OPAQUE SUMMARY:Floating Time event, starting 05:30am Europe/Berlin DTSTART:20141108T053000 DTSTAMP:20140701T143706Z SEQUENCE:1 END:VEVENT END:VCALENDAR ', ), ), ); function testExpandCalendarQuery() { $request = new HTTP\Request('REPORT', '/calendars/user1/calendar1', [ 'Depth' => 1, 'Content-Type' => 'application/xml', ]); $request->setBody(' '); $response = $this->request($request); // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); // check if DTSTARTs and DTENDs are correct foreach ($vObject->VEVENT as $vevent) { /** @var $vevent Sabre\VObject\Component\VEvent */ foreach ($vevent->children as $child) { /** @var $child Sabre\VObject\Property */ if ($child->name == 'DTSTART') { // DTSTART should be the UTC equivalent of given floating time $this->assertEquals($child->getValue(), '20141108T043000Z'); } elseif ($child->name == 'DTEND') { // DTEND should be the UTC equivalent of given floating time $this->assertEquals($child->getValue(), '20141108T063000Z'); } } } } function testExpandMultiGet() { $request = new HTTP\Request('REPORT', '/calendars/user1/calendar1', [ 'Depth' => 1, 'Content-Type' => 'application/xml', ]); $request->setBody(' /calendars/user1/calendar1/event.ics '); $response = $this->request($request); $this->assertEquals(207, $response->getStatus()); // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); // check if DTSTARTs and DTENDs are correct foreach ($vObject->VEVENT as $vevent) { /** @var $vevent Sabre\VObject\Component\VEvent */ foreach ($vevent->children as $child) { /** @var $child Sabre\VObject\Property */ if ($child->name == 'DTSTART') { // DTSTART should be the UTC equivalent of given floating time $this->assertEquals($child->getValue(), '20141108T043000Z'); } elseif ($child->name == 'DTEND') { // DTEND should be the UTC equivalent of given floating time $this->assertEquals($child->getValue(), '20141108T063000Z'); } } } } function testExpandExport() { $request = new HTTP\Request('GET', '/calendars/user1/calendar1?export&start=1&end=2000000000&expand=1', [ 'Depth' => 1, 'Content-Type' => 'application/xml', ]); $response = $this->request($request); $this->assertEquals(200, $response->getStatus()); // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); // check if DTSTARTs and DTENDs are correct foreach ($vObject->VEVENT as $vevent) { /** @var $vevent Sabre\VObject\Component\VEvent */ foreach ($vevent->children as $child) { /** @var $child Sabre\VObject\Property */ if ($child->name == 'DTSTART') { // DTSTART should be the UTC equivalent of given floating time $this->assertEquals($child->getValue(), '20141108T043000Z'); } elseif ($child->name == 'DTEND') { // DTEND should be the UTC equivalent of given floating time $this->assertEquals($child->getValue(), '20141108T063000Z'); } } } } } sabre-dav-2.1.10/tests/Sabre/CalDAV/FreeBusyReportTest.php000066400000000000000000000112001267035675400231710ustar00rootroot00000000000000 [ 'obj1' => [ 'calendarid' => 1, 'uri' => 'event1.ics', 'calendardata' => $obj1, ], 'obj2' => [ 'calendarid' => 1, 'uri' => 'event2.ics', 'calendardata' => $obj2 ], 'obj3' => [ 'calendarid' => 1, 'uri' => 'event3.ics', 'calendardata' => $obj3 ] ], ]; $caldavBackend = new Backend\Mock([], $calendarData); $calendar = new Calendar($caldavBackend, [ 'id' => 1, 'uri' => 'calendar', 'principaluri' => 'principals/user1', '{' . Plugin::NS_CALDAV . '}calendar-timezone' => "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nEND:VTIMEZONE\r\nEND:VCALENDAR", ]); $this->server = new DAV\Server([$calendar]); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/calendar', ]); $this->server->httpRequest = $request; $this->server->httpResponse = new HTTP\ResponseMock(); $this->plugin = new Plugin(); $this->server->addPlugin($this->plugin); } function testFreeBusyReport() { $reportXML = << XML; $dom = DAV\XMLUtil::loadDOMDocument($reportXML); $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom); $this->assertEquals(200, $this->server->httpResponse->status); $this->assertEquals('text/calendar', $this->server->httpResponse->getHeader('Content-Type')); $this->assertTrue(strpos($this->server->httpResponse->body, 'BEGIN:VFREEBUSY')!==false); $this->assertTrue(strpos($this->server->httpResponse->body, '20111005T120000Z/20111005T130000Z')!==false); $this->assertTrue(strpos($this->server->httpResponse->body, '20111006T100000Z/20111006T110000Z')!==false); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testFreeBusyReportNoTimeRange() { $reportXML = << XML; $dom = DAV\XMLUtil::loadDOMDocument($reportXML); $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom); } /** * @expectedException Sabre\DAV\Exception\NotImplemented */ function testFreeBusyReportWrongNode() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_URI' => '/', )); $this->server->httpRequest = $request; $reportXML = << XML; $dom = DAV\XMLUtil::loadDOMDocument($reportXML); $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom); } /** * @expectedException Sabre\DAV\Exception */ function testFreeBusyReportNoACLPlugin() { $this->server = new DAV\Server(); $this->plugin = new Plugin(); $this->server->addPlugin($this->plugin); $reportXML = << XML; $dom = DAV\XMLUtil::loadDOMDocument($reportXML); $this->plugin->report('{urn:ietf:params:xml:ns:caldav}free-busy-query', $dom); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/GetEventsByTimerangeTest.php000066400000000000000000000051351267035675400243160ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT CREATED:20120313T142342Z UID:171EBEFC-C951-499D-B234-7BA7D677B45D DTEND;TZID=Europe/Berlin:20120227T010000 TRANSP:OPAQUE SUMMARY:Monday 0h DTSTART;TZID=Europe/Berlin:20120227T000000 DTSTAMP:20120313T142416Z SEQUENCE:4 END:VEVENT END:VCALENDAR ', ), ), ); function testQueryTimerange() { $request = new HTTP\Request( 'REPORT', '/calendars/user1/calendar1', [ 'Content-Type' => 'application/xml', 'Depth' => '1', ] ); $request->setBody(' '); $response = $this->request($request); if (strpos($response->body, 'BEGIN:VCALENDAR') === false) { $this->fail('Got no events instead of 1. Output: '.$response->body); } // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); // We expect 1 event $this->assertEquals(1, count($vObject->VEVENT), 'We got 0 events instead of 1. Output: ' . $body); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/ICSExportPluginTest.php000066400000000000000000000361401267035675400232620ustar00rootroot00000000000000markTestSkipped('SQLite driver is not available'); } function testInit() { $p = new ICSExportPlugin(); $s = new DAV\Server(); $s->addPlugin($p); $this->assertEquals($p, $s->getPlugin('Sabre\CalDAV\ICSExportPlugin')); } function testBeforeMethod() { $cbackend = TestUtil::getBackend(); $props = [ 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, '{DAV:}displayname' => 'Hello!', '{http://apple.com/ns/ical/}calendar-color' => '#AA0000FF', ]; $tree = [ new Calendar($cbackend,$props), ]; $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $this->assertFalse($p->httpGet($h, $s->httpResponse)); $this->assertEquals(200, $s->httpResponse->status); $this->assertEquals([ 'Content-Type' => ['text/calendar'], ], $s->httpResponse->getHeaders()); $obj = VObject\Reader::read($s->httpResponse->body); $this->assertEquals(7,count($obj->children())); $this->assertEquals(1,count($obj->VERSION)); $this->assertEquals(1,count($obj->CALSCALE)); $this->assertEquals(1,count($obj->PRODID)); $this->assertTrue(strpos((string)$obj->PRODID, DAV\Version::VERSION)!==false); $this->assertEquals(1,count($obj->VTIMEZONE)); $this->assertEquals(1,count($obj->VEVENT)); $this->assertEquals("Hello!", $obj->{"X-WR-CALNAME"}); $this->assertEquals("#AA0000FF", $obj->{"X-APPLE-CALENDAR-COLOR"}); } function testBeforeMethodNoVersion() { if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available'); $cbackend = TestUtil::getBackend(); $props = [ 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ]; $tree = [ new Calendar($cbackend,$props), ]; $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); DAV\Server::$exposeVersion = false; $this->assertFalse($p->httpGet($h, $s->httpResponse)); DAV\Server::$exposeVersion = true; $this->assertEquals(200, $s->httpResponse->status); $this->assertEquals([ 'Content-Type' => ['text/calendar'], ], $s->httpResponse->getHeaders()); $obj = VObject\Reader::read($s->httpResponse->body); $this->assertEquals(5,count($obj->children())); $this->assertEquals(1,count($obj->VERSION)); $this->assertEquals(1,count($obj->CALSCALE)); $this->assertEquals(1,count($obj->PRODID)); $this->assertFalse(strpos((string)$obj->PRODID, DAV\Version::VERSION)!==false); $this->assertEquals(1,count($obj->VTIMEZONE)); $this->assertEquals(1,count($obj->VEVENT)); } function testBeforeMethodNoExport() { $p = new ICSExportPlugin(); $s = new DAV\Server(); $s->addPlugin($p); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467', 'REQUEST_METHOD' => 'GET', ]); $this->assertNull($p->httpGet($h, $s->httpResponse)); } function testACLIntegrationBlocked() { $cbackend = TestUtil::getBackend(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->addPlugin($p); $s->addPlugin(new Plugin()); $s->addPlugin(new DAVACL\Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $p->httpGet($h, $s->httpResponse); // If the ACL system blocked this request, the effect will be that // there's no response, because the calendar information could not be // fetched. $this->assertNull($s->httpResponse->getStatus()); } function testACLIntegrationNotBlocked() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $s->addPlugin(new DAVACL\Plugin()); $s->addPlugin(new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV')); // Forcing login $s->getPlugin('acl')->adminPrincipals = array('principals/admin'); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['text/calendar'], ), $s->httpResponse->getHeaders()); $obj = VObject\Reader::read($s->httpResponse->body); $this->assertEquals(5,count($obj->children())); $this->assertEquals(1,count($obj->VERSION)); $this->assertEquals(1,count($obj->CALSCALE)); $this->assertEquals(1,count($obj->PRODID)); $this->assertEquals(1,count($obj->VTIMEZONE)); $this->assertEquals(1,count($obj->VEVENT)); } function testBadStartParam() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export&start=foo', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(400, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); } function testBadEndParam() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export&end=foo', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(400, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); } function testFilterStartEnd() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export&start=1&end=2', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); $obj = VObject\Reader::read($s->httpResponse->body); $this->assertEquals(0,count($obj->VTIMEZONE)); $this->assertEquals(0,count($obj->VEVENT)); } function testExpandNoStart() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export&expand=1&end=1', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(400, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); } function testExpand() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export&start=1&end=2000000000&expand=1', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); $obj = VObject\Reader::read($s->httpResponse->body); $this->assertEquals(0,count($obj->VTIMEZONE)); $this->assertEquals(1,count($obj->VEVENT)); } function testJCal() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export', 'REQUEST_METHOD' => 'GET', 'HTTP_ACCEPT' => 'application/calendar+json', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); $this->assertEquals('application/calendar+json', $s->httpResponse->getHeader('Content-Type')); } function testJCalInUrl() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export&accept=jcal', 'REQUEST_METHOD' => 'GET', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); $this->assertEquals('application/calendar+json', $s->httpResponse->getHeader('Content-Type')); } function testNegotiateDefault() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array( 'uri'=>'UUID-123467', 'principaluri' => 'admin', 'id' => 1, ); $tree = array( new Calendar($cbackend,$props), new DAVACL\PrincipalCollection($pbackend), ); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $h = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/UUID-123467?export', 'REQUEST_METHOD' => 'GET', 'HTTP_ACCEPT' => 'text/plain', ]); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(200, $s->httpResponse->status,'Invalid status received. Response body: '. $s->httpResponse->body); $this->assertEquals('text/calendar', $s->httpResponse->getHeader('Content-Type')); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Issue166Test.php000066400000000000000000000030201267035675400215770ustar00rootroot00000000000000 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2011-12-01'), 'end' => new \DateTime('2012-02-01'), ), ), ), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null, ); $input = VObject\Reader::read($input); $this->assertTrue($validator->validate($input,$filters)); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Issue172Test.php000066400000000000000000000077571267035675400216210ustar00rootroot00000000000000 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2012-01-18 21:00:00 GMT-08:00'), 'end' => new \DateTime('2012-01-18 21:00:00 GMT-08:00'), ), ), ), 'prop-filters' => array(), ); $input = VObject\Reader::read($input); $this->assertTrue($validator->validate($input,$filters)); } // Pacific Standard Time, translates to America/Los_Angeles (GMT-8 in January) function testOutlookTimezoneName() { $input = << 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2012-01-13 10:30:00 GMT-08:00'), 'end' => new \DateTime('2012-01-13 10:30:00 GMT-08:00'), ), ), ), 'prop-filters' => array(), ); $input = VObject\Reader::read($input); $this->assertTrue($validator->validate($input,$filters)); } // X-LIC-LOCATION, translates to America/Los_Angeles (GMT-8 in January) function testLibICalLocationName() { $input = << 'VCALENDAR', 'comp-filters' => array( array( 'name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array( 'start' => new \DateTime('2012-01-13 10:30:00 GMT-08:00'), 'end' => new \DateTime('2012-01-13 10:30:00 GMT-08:00'), ), ), ), 'prop-filters' => array(), ); $input = VObject\Reader::read($input); $this->assertTrue($validator->validate($input,$filters)); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Issue203Test.php000066400000000000000000000077271267035675400216110ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:20120330T155305CEST-6585fBUVgV DTSTAMP:20120330T135305Z DTSTART;TZID=Europe/Berlin:20120326T155200 DTEND;TZID=Europe/Berlin:20120326T165200 RRULE:FREQ=DAILY;COUNT=2;INTERVAL=1 SUMMARY:original summary TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT UID:20120330T155305CEST-6585fBUVgV DTSTAMP:20120330T135352Z DESCRIPTION: DTSTART;TZID=Europe/Berlin:20120328T155200 DTEND;TZID=Europe/Berlin:20120328T165200 RECURRENCE-ID;TZID=Europe/Berlin:20120327T155200 SEQUENCE:1 SUMMARY:overwritten summary TRANSP:OPAQUE END:VEVENT END:VCALENDAR ', ), ), ); function testIssue203() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/calendars/user1/calendar1', 'HTTP_DEPTH' => '1', )); $request->setBody(' '); $response = $this->request($request); // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); $this->assertEquals(2, count($vObject->VEVENT)); $expectedEvents = array( array( 'DTSTART' => '20120326T135200Z', 'DTEND' => '20120326T145200Z', 'SUMMARY' => 'original summary', ), array( 'DTSTART' => '20120328T135200Z', 'DTEND' => '20120328T145200Z', 'SUMMARY' => 'overwritten summary', 'RECURRENCE-ID' => '20120327T135200Z', ) ); // try to match agains $expectedEvents array foreach ($expectedEvents as $expectedEvent) { $matching = false; foreach ($vObject->VEVENT as $vevent) { /** @var $vevent Sabre\VObject\Component\VEvent */ foreach ($vevent->children as $child) { /** @var $child Sabre\VObject\Property */ if (isset($expectedEvent[$child->name])) { if ($expectedEvent[$child->name] != $child->getValue()) { continue 2; } } } $matching = true; break; } $this->assertTrue($matching, 'Did not find the following event in the response: '.var_export($expectedEvent, true)); } } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Issue205Test.php000066400000000000000000000053521267035675400216030ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:20120330T155305CEST-6585fBUVgV DTSTAMP:20120330T135305Z DTSTART;TZID=Europe/Berlin:20120326T155200 DTEND;TZID=Europe/Berlin:20120326T165200 SUMMARY:original summary TRANSP:OPAQUE BEGIN:VALARM ACTION:AUDIO ATTACH;VALUE=URI:Basso TRIGGER:PT0S END:VALARM END:VEVENT END:VCALENDAR ', ), ), ); function testIssue205() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/calendars/user1/calendar1', 'HTTP_DEPTH' => '1', )); $request->setBody(' '); $response = $this->request($request); $this->assertFalse(strpos($response->body, 'Exception'), 'Exception occurred: ' . $response->body); $this->assertFalse(strpos($response->body, 'Unknown or bad format'), 'DateTime unknown format Exception: ' . $response->body); // Everts super awesome xml parser. $body = substr( $response->body, $start = strpos($response->body, 'BEGIN:VCALENDAR'), strpos($response->body, 'END:VCALENDAR') - $start + 13 ); $body = str_replace(' ','',$body); $vObject = VObject\Reader::read($body); $this->assertEquals(1, count($vObject->VEVENT)); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Issue211Test.php000066400000000000000000000045271267035675400216030ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:20120418T172519CEST-3510gh1hVw DTSTAMP:20120418T152519Z DTSTART;VALUE=DATE:20120330 DTEND;VALUE=DATE:20120531 EXDATE;TZID=Europe/Berlin:20120330T000000 RRULE:FREQ=YEARLY;INTERVAL=1 SEQUENCE:1 SUMMARY:Birthday TRANSP:TRANSPARENT BEGIN:VALARM ACTION:EMAIL ATTENDEE:MAILTO:xxx@domain.de DESCRIPTION:Dies ist eine Kalender Erinnerung SUMMARY:Kalender Alarm Erinnerung TRIGGER;VALUE=DATE-TIME:20120329T060000Z END:VALARM END:VEVENT END:VCALENDAR ', ), ), ); function testIssue211() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/calendars/user1/calendar1', 'HTTP_DEPTH' => '1', )); $request->setBody(' '); $response = $this->request($request); // if this assert is reached, the endless loop is gone // There should be no matching events $this->assertFalse(strpos('BEGIN:VEVENT', $response->body)); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Issue220Test.php000066400000000000000000000054001267035675400215720ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT DTSTART;TZID=Europe/Berlin:20120601T180000 SUMMARY:Brot backen RRULE:FREQ=DAILY;INTERVAL=1;WKST=MO TRANSP:OPAQUE DURATION:PT20M LAST-MODIFIED:20120601T064634Z CREATED:20120601T064634Z DTSTAMP:20120601T064634Z UID:b64f14c5-dccc-4eda-947f-bdb1f763fbcd BEGIN:VALARM TRIGGER;VALUE=DURATION:-PT5M ACTION:DISPLAY DESCRIPTION:Default Event Notification X-WR-ALARMUID:cd952c1b-b3d6-41fb-b0a6-ec3a1a5bdd58 END:VALARM END:VEVENT BEGIN:VEVENT DTSTART;TZID=Europe/Berlin:20120606T180000 SUMMARY:Brot backen TRANSP:OPAQUE STATUS:CANCELLED DTEND;TZID=Europe/Berlin:20120606T182000 LAST-MODIFIED:20120605T094310Z SEQUENCE:1 RECURRENCE-ID:20120606T160000Z UID:b64f14c5-dccc-4eda-947f-bdb1f763fbcd END:VEVENT END:VCALENDAR ', ), ), ); function testIssue220() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/calendars/user1/calendar1', 'HTTP_DEPTH' => '1', )); $request->setBody(' '); $response = $this->request($request); $this->assertFalse(strpos($response->body, 'PHPUnit_Framework_Error_Warning'), 'Error Warning occurred: ' . $response->body); $this->assertFalse(strpos($response->body, 'Invalid argument supplied for foreach()'), 'Invalid argument supplied for foreach(): ' . $response->body); $this->assertEquals(207, $response->status); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Issue228Test.php000066400000000000000000000040131267035675400216010ustar00rootroot00000000000000 1, 'name' => 'Calendar', 'principaluri' => 'principals/user1', 'uri' => 'calendar1', ) ); protected $caldavCalendarObjects = array( 1 => array( 'event.ics' => array( 'calendardata' => 'BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT UID:20120730T113415CEST-6804EGphkd@xxxxxx.de DTSTAMP:20120730T093415Z DTSTART;VALUE=DATE:20120729 DTEND;VALUE=DATE:20120730 SUMMARY:sunday event TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR ', ), ), ); function testIssue228() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/calendars/user1/calendar1', 'HTTP_DEPTH' => '1', )); $request->setBody(' '); $response = $this->request($request); // We must check if absolutely nothing was returned from this query. $this->assertFalse(strpos($response->body, 'BEGIN:VCALENDAR')); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/JCalTransformTest.php000066400000000000000000000161741267035675400227750ustar00rootroot00000000000000 1, 'principaluri' => 'principals/user1', 'uri' => 'foo', ] ]; protected $caldavCalendarObjects = [ 1 => [ 'bar.ics' => [ 'uri' => 'bar.ics', 'calendarid' => 1, 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", 'lastmodified' => null ] ], ]; function testGet() { $headers = [ 'Accept' => 'application/calendar+json', ]; $request = new Request('GET', '/calendars/user1/foo/bar.ics', $headers); $response = $this->request($request); $body = $response->getBodyAsString(); $this->assertEquals(200, $response->getStatus(), "Incorrect status code: " . $body); $response = json_decode($body,true); if (json_last_error() !== JSON_ERROR_NONE) { $this->fail('Json decoding error: ' . json_last_error_msg()); } $this->assertEquals( [ 'vcalendar', [], [ [ 'vevent', [], [], ], ], ], $response ); } function testMultiGet() { $xml = << /calendars/user1/foo/bar.ics XML; $headers = []; $request = new Request('REPORT', '/calendars/user1/foo', $headers, $xml); $response = $this->request($request); $this->assertEquals(207, $response->getStatus(), 'Full rsponse: ' . $response->getBodyAsString()); $body = $response->getBodyAsString(); // Getting from the xml body to the actual returned data is // unfortunately very convoluted. $responses = \Sabre\DAV\Property\ResponseList::unserialize( \Sabre\DAV\XMLUtil::loadDOMDocument($body)->firstChild , $this->server->propertyMap); $responses = $responses->getResponses(); $this->assertEquals(1, count($responses)); $response = $responses[0]->getResponseProperties()[200]["{urn:ietf:params:xml:ns:caldav}calendar-data"]; $response = json_decode($response,true); if (json_last_error()) { $this->fail('Json decoding error: ' . json_last_error_msg()); } $this->assertEquals( [ 'vcalendar', [], [ [ 'vevent', [], [], ], ], ], $response ); } function testCalendarQueryDepth1() { $xml = << XML; $headers = [ 'Depth' => '1', ]; $request = new Request('REPORT', '/calendars/user1/foo', $headers, $xml); $response = $this->request($request); $this->assertEquals(207, $response->getStatus(), "Invalid response code. Full body: " . $response->getBodyAsString()); $body = $response->getBodyAsString(); // Getting from the xml body to the actual returned data is // unfortunately very convoluted. $responses = \Sabre\DAV\Property\ResponseList::unserialize( \Sabre\DAV\XMLUtil::loadDOMDocument($body)->firstChild , $this->server->propertyMap); $responses = $responses->getResponses(); $this->assertEquals(1, count($responses)); $response = $responses[0]->getResponseProperties()[200]["{urn:ietf:params:xml:ns:caldav}calendar-data"]; $response = json_decode($response,true); if (json_last_error()) { $this->fail('Json decoding error: ' . json_last_error_msg()); } $this->assertEquals( [ 'vcalendar', [], [ [ 'vevent', [], [], ], ], ], $response ); } function testCalendarQueryDepth0() { $xml = << XML; $headers = [ 'Depth' => '0', ]; $request = new Request('REPORT', '/calendars/user1/foo/bar.ics', $headers, $xml); $response = $this->request($request); $this->assertEquals(207, $response->getStatus(), "Invalid response code. Full body: " . $response->getBodyAsString()); $body = $response->getBodyAsString(); // Getting from the xml body to the actual returned data is // unfortunately very convoluted. $responses = \Sabre\DAV\Property\ResponseList::unserialize( \Sabre\DAV\XMLUtil::loadDOMDocument($body)->firstChild , $this->server->propertyMap); $responses = $responses->getResponses(); $this->assertEquals(1, count($responses)); $response = $responses[0]->getResponseProperties()[200]["{urn:ietf:params:xml:ns:caldav}calendar-data"]; $response = json_decode($response,true); if (json_last_error()) { $this->fail('Json decoding error: ' . json_last_error_msg()); } $this->assertEquals( [ 'vcalendar', [], [ [ 'vevent', [], [], ], ], ], $response ); } function testValidateICalendar() { $input = [ 'vcalendar', [], [ [ 'vevent', [ ['uid', (object)[], 'text', 'foo'], ], [], ], ], ]; $input = json_encode($input); $this->caldavPlugin->beforeWriteContent( 'calendars/user1/foo/bar.ics', $this->server->tree->getNodeForPath('calendars/user1/foo/bar.ics'), $input, $modified ); $this->assertEquals("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", $input); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Notifications/000077500000000000000000000000001267035675400215175ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Notifications/CollectionTest.php000066400000000000000000000037721267035675400251740ustar00rootroot00000000000000principalUri = 'principals/user1'; $this->notification = new Notification\SystemStatus(1,'"1"'); $this->caldavBackend = new CalDAV\Backend\MockSharing(array(),array(), array( 'principals/user1' => array( $this->notification ) )); return new Collection($this->caldavBackend, $this->principalUri); } function testGetChildren() { $col = $this->getInstance(); $this->assertEquals('notifications', $col->getName()); $this->assertEquals(array( new Node($this->caldavBackend, $this->principalUri, $this->notification) ), $col->getChildren()); } function testGetOwner() { $col = $this->getInstance(); $this->assertEquals('principals/user1', $col->getOwner()); } function testGetGroup() { $col = $this->getInstance(); $this->assertNull($col->getGroup()); } function testGetACL() { $col = $this->getInstance(); $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => $this->principalUri, 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => $this->principalUri, 'protected' => true, ), ); $this->assertEquals($expected, $col->getACL()); } /** * @expectedException Sabre\DAV\Exception\NotImplemented */ function testSetACL() { $col = $this->getInstance(); $col->setACL(array()); } function testGetSupportedPrivilegeSet() { $col = $this->getInstance(); $this->assertNull($col->getSupportedPrivilegeSet()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Notifications/NodeTest.php000066400000000000000000000043401267035675400237560ustar00rootroot00000000000000systemStatus = new Notification\SystemStatus(1,'"1"'); $this->caldavBackend = new CalDAV\Backend\MockSharing(array(),array(), array( 'principals/user1' => array( $this->systemStatus ) )); $node = new Node($this->caldavBackend, 'principals/user1', $this->systemStatus); return $node; } function testGetId() { $node = $this->getInstance(); $this->assertEquals($this->systemStatus->getId() . '.xml', $node->getName()); } function testGetEtag() { $node = $this->getInstance(); $this->assertEquals('"1"', $node->getETag()); } function testGetNotificationType() { $node = $this->getInstance(); $this->assertEquals($this->systemStatus, $node->getNotificationType()); } function testDelete() { $node = $this->getInstance(); $node->delete(); $this->assertEquals(array(), $this->caldavBackend->getNotificationsForPrincipal('principals/user1')); } function testGetGroup() { $node = $this->getInstance(); $this->assertNull($node->getGroup()); } function testGetACL() { $node = $this->getInstance(); $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), ); $this->assertEquals($expected, $node->getACL()); } /** * @expectedException Sabre\DAV\Exception\NotImplemented */ function testSetACL() { $node = $this->getInstance(); $node->setACL(array()); } function testGetSupportedPrivilegeSet() { $node = $this->getInstance(); $this->assertNull($node->getSupportedPrivilegeSet()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Notifications/Notification/000077500000000000000000000000001267035675400241455ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Notifications/Notification/InviteReplyTest.php000066400000000000000000000072471267035675400300020ustar00rootroot00000000000000assertEquals('foo', $notification->getId()); $this->assertEquals('"1"', $notification->getETag()); $simpleExpected = '' . "\n" . '' . "\n"; $dom = new \DOMDocument('1.0','UTF-8'); $elem = $dom->createElement('cs:root'); $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $dom->appendChild($elem); $notification->serialize(new DAV\Server(), $elem); $this->assertEquals($simpleExpected, $dom->saveXML()); $dom = new \DOMDocument('1.0','UTF-8'); $dom->formatOutput = true; $elem = $dom->createElement('cs:root'); $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $elem->setAttribute('xmlns:d','DAV:'); $dom->appendChild($elem); $notification->serializeBody(new DAV\Server(), $elem); $this->assertEquals($expected, $dom->saveXML()); } function dataProvider() { $dtStamp = new \DateTime('2012-01-01 00:00:00 GMT'); return array( array( array( 'id' => 'foo', 'dtStamp' => $dtStamp, 'etag' => '"1"', 'inReplyTo' => 'bar', 'href' => 'mailto:foo@example.org', 'type' => CalDAV\SharingPlugin::STATUS_ACCEPTED, 'hostUrl' => 'calendar' ), << 20120101T000000Z foo bar mailto:foo@example.org /calendar FOO ), array( array( 'id' => 'foo', 'dtStamp' => $dtStamp, 'etag' => '"1"', 'inReplyTo' => 'bar', 'href' => 'mailto:foo@example.org', 'type' => CalDAV\SharingPlugin::STATUS_DECLINED, 'hostUrl' => 'calendar', 'summary' => 'Summary!' ), << 20120101T000000Z foo bar mailto:foo@example.org /calendar Summary! FOO ), ); } /** * @expectedException InvalidArgumentException */ function testMissingArg() { new InviteReply(array()); } /** * @expectedException InvalidArgumentException */ function testUnknownArg() { new InviteReply(array( 'foo-i-will-break' => true, 'id' => 1, 'etag' => '"bla"', 'href' => 'abc', 'dtStamp' => 'def', 'inReplyTo' => 'qrs', 'type' => 'ghi', 'hostUrl' => 'jkl', )); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Notifications/Notification/InviteTest.php000066400000000000000000000157401267035675400267630ustar00rootroot00000000000000assertEquals('foo', $notification->getId()); $this->assertEquals('"1"', $notification->getETag()); $simpleExpected = '' . "\n" . '' . "\n"; $dom = new \DOMDocument('1.0','UTF-8'); $elem = $dom->createElement('cs:root'); $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $dom->appendChild($elem); $notification->serialize(new DAV\Server(), $elem); $this->assertEquals($simpleExpected, $dom->saveXML()); $dom = new \DOMDocument('1.0','UTF-8'); $dom->formatOutput = true; $elem = $dom->createElement('cs:root'); $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $elem->setAttribute('xmlns:d','DAV:'); $elem->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV); $dom->appendChild($elem); $notification->serializeBody(new DAV\Server(), $elem); $this->assertEquals($expected, $dom->saveXML()); } function dataProvider() { $dtStamp = new \DateTime('2012-01-01 00:00:00', new \DateTimeZone('GMT')); return array( array( array( 'id' => 'foo', 'dtStamp' => $dtStamp, 'etag' => '"1"', 'href' => 'mailto:foo@example.org', 'type' => CalDAV\SharingPlugin::STATUS_ACCEPTED, 'readOnly' => true, 'hostUrl' => 'calendar', 'organizer' => 'principal/user1', 'commonName' => 'John Doe', 'summary' => 'Awesome stuff!' ), << 20120101T000000Z foo mailto:foo@example.org /calendar John Doe /principal/user1 John Doe Awesome stuff! FOO ), array( array( 'id' => 'foo', 'dtStamp' => $dtStamp, 'etag' => '"1"', 'href' => 'mailto:foo@example.org', 'type' => CalDAV\SharingPlugin::STATUS_DECLINED, 'readOnly' => true, 'hostUrl' => 'calendar', 'organizer' => 'principal/user1', 'commonName' => 'John Doe', ), << 20120101T000000Z foo mailto:foo@example.org /calendar John Doe /principal/user1 John Doe FOO ), array( array( 'id' => 'foo', 'dtStamp' => $dtStamp, 'etag' => '"1"', 'href' => 'mailto:foo@example.org', 'type' => CalDAV\SharingPlugin::STATUS_NORESPONSE, 'readOnly' => true, 'hostUrl' => 'calendar', 'organizer' => 'principal/user1', 'firstName' => 'Foo', 'lastName' => 'Bar', ), << 20120101T000000Z foo mailto:foo@example.org /calendar Foo Bar /principal/user1 Foo Bar FOO ), array( array( 'id' => 'foo', 'dtStamp' => $dtStamp, 'etag' => '"1"', 'href' => 'mailto:foo@example.org', 'type' => CalDAV\SharingPlugin::STATUS_DELETED, 'readOnly' => false, 'hostUrl' => 'calendar', 'organizer' => 'mailto:user1@fruux.com', 'supportedComponents' => new CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')), ), << 20120101T000000Z foo mailto:foo@example.org /calendar mailto:user1@fruux.com FOO ), ); } /** * @expectedException InvalidArgumentException */ function testMissingArg() { new Invite(array()); } /** * @expectedException InvalidArgumentException */ function testUnknownArg() { new Invite(array( 'foo-i-will-break' => true, 'id' => 1, 'etag' => '"bla"', 'href' => 'abc', 'dtStamp' => 'def', 'type' => 'ghi', 'readOnly' => true, 'hostUrl' => 'jkl', 'organizer' => 'mno', )); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Notifications/Notification/SystemStatusTest.php000066400000000000000000000050141267035675400302060ustar00rootroot00000000000000assertEquals('foo', $notification->getId()); $this->assertEquals('"1"', $notification->getETag()); $dom = new \DOMDocument('1.0','UTF-8'); $elem = $dom->createElement('cs:root'); $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $dom->appendChild($elem); $notification->serialize(new DAV\Server(), $elem); $this->assertEquals($expected1, $dom->saveXML()); $dom = new \DOMDocument('1.0','UTF-8'); $elem = $dom->createElement('cs:root'); $elem->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $dom->appendChild($elem); $notification->serializeBody(new DAV\Server(), $elem); $this->assertEquals($expected2, $dom->saveXML()); } function dataProvider() { return array( array( new SystemStatus('foo', '"1"'), '' . "\n" . '' . "\n", '' . "\n" . '' . "\n", ), array( new SystemStatus('foo', '"1"', SystemStatus::TYPE_MEDIUM,'bar'), '' . "\n" . '' . "\n", '' . "\n" . 'bar' . "\n", ), array( new SystemStatus('foo', '"1"', SystemStatus::TYPE_LOW,null,'http://example.org/'), '' . "\n" . '' . "\n", '' . "\n" . 'http://example.org/' . "\n", ) ); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Notifications/PluginTest.php000066400000000000000000000114321267035675400243270ustar00rootroot00000000000000caldavBackend = new CalDAV\Backend\MockSharing(); $principalBackend = new DAVACL\PrincipalBackend\Mock(); $calendars = new CalDAV\CalendarRoot($principalBackend,$this->caldavBackend); $principals = new CalDAV\Principal\Collection($principalBackend); $root = new DAV\SimpleCollection('root'); $root->addChild($calendars); $root->addChild($principals); $this->server = new DAV\Server($root); $this->server->sapi = new HTTP\SapiMock(); $this->server->debugExceptions = true; $this->server->setBaseUri('/'); $this->plugin = new Plugin(); $this->server->addPlugin($this->plugin); // Adding ACL plugin $this->server->addPlugin(new DAVACL\Plugin()); // CalDAV is also required. $this->server->addPlugin(new CalDAV\Plugin()); // Adding Auth plugin, and ensuring that we are logged in. $authBackend = new DAV\Auth\Backend\Mock(); $authBackend->defaultUser = 'user1'; $authPlugin = new DAV\Auth\Plugin($authBackend, 'SabreDAV'); $this->server->addPlugin($authPlugin); // This forces a login $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); $this->response = new HTTP\ResponseMock(); $this->server->httpResponse = $this->response; } function testSimple() { $this->assertEquals([], $this->plugin->getFeatures()); $this->assertEquals('notifications', $this->plugin->getPluginName()); } function testPrincipalProperties() { $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_HOST' => 'sabredav.org', )); $this->server->httpRequest = $httpRequest; $props = $this->server->getPropertiesForPath('/principals/user1',array( '{' . Plugin::NS_CALENDARSERVER . '}notification-URL', )); $this->assertArrayHasKey(0,$props); $this->assertArrayHasKey(200,$props[0]); $this->assertArrayHasKey('{'.Plugin::NS_CALENDARSERVER .'}notification-URL',$props[0][200]); $prop = $props[0][200]['{'.Plugin::NS_CALENDARSERVER .'}notification-URL']; $this->assertTrue($prop instanceof DAV\Property\Href); $this->assertEquals('calendars/user1/notifications/', $prop->getHref()); } function testNotificationProperties() { $notification = new Node( $this->caldavBackend, 'principals/user1', new Notification\SystemStatus('foo','"1"') ); $propFind = new DAV\PropFind('calendars/user1/notifications', [ '{' . Plugin::NS_CALENDARSERVER . '}notificationtype', ]); $this->plugin->propFind($propFind, $notification); $this->assertEquals( $notification->getNotificationType(), $propFind->get('{' . Plugin::NS_CALENDARSERVER . '}notificationtype') ); } function testNotificationGet() { $notification = new Node( $this->caldavBackend, 'principals/user1', new Notification\SystemStatus('foo','"1"') ); $server = new DAV\Server(array($notification)); $caldav = new Plugin(); $server->httpRequest = HTTP\Sapi::createFromServerArray(array( 'REQUEST_URI' => '/foo.xml', )); $httpResponse = new HTTP\ResponseMock(); $server->httpResponse = $httpResponse; $server->addPlugin($caldav); $caldav->httpGet($server->httpRequest, $server->httpResponse); $this->assertEquals(200, $httpResponse->status); $this->assertEquals(array( 'Content-Type' => ['application/xml'], 'ETag' => ['"1"'], ), $httpResponse->getHeaders()); $expected = ' '; $this->assertEquals($expected, $httpResponse->body); } function testGETPassthrough() { $server = new DAV\Server(); $caldav = new Plugin(); $httpResponse = new HTTP\ResponseMock(); $server->httpResponse = $httpResponse; $server->addPlugin($caldav); $this->assertNull($caldav->httpGet(new HTTP\Request('GET','/foozz'), $server->httpResponse)); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/PluginTest.php000066400000000000000000001162161267035675400215240ustar00rootroot00000000000000caldavBackend = new Backend\Mock(array( array( 'id' => 1, 'uri' => 'UUID-123467', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'user1 calendar', '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description', '{http://apple.com/ns/ical/}calendar-order' => '1', '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')), ), array( 'id' => 2, 'uri' => 'UUID-123468', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'user1 calendar2', '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description', '{http://apple.com/ns/ical/}calendar-order' => '1', '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Property\SupportedCalendarComponentSet(array('VEVENT','VTODO')), ) ), array( 1 => array( 'UUID-2345' => array( 'calendardata' => TestUtil::getTestCalendarData(), ) ) )); $principalBackend = new DAVACL\PrincipalBackend\Mock(); $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-read',array('principals/user1')); $principalBackend->setGroupMemberSet('principals/admin/calendar-proxy-write',array('principals/user1')); $principalBackend->addPrincipal(array( 'uri' => 'principals/admin/calendar-proxy-read', )); $principalBackend->addPrincipal(array( 'uri' => 'principals/admin/calendar-proxy-write', )); $calendars = new CalendarRoot($principalBackend,$this->caldavBackend); $principals = new Principal\Collection($principalBackend); $root = new DAV\SimpleCollection('root'); $root->addChild($calendars); $root->addChild($principals); $this->server = new DAV\Server($root); $this->server->sapi = new HTTP\SapiMock(); $this->server->debugExceptions = true; $this->server->setBaseUri('/'); $this->plugin = new Plugin(); $this->server->addPlugin($this->plugin); // Adding ACL plugin $this->server->addPlugin(new DAVACL\Plugin()); // Adding Auth plugin, and ensuring that we are logged in. $authBackend = new DAV\Auth\Backend\Mock(); $authBackend->defaultUser = 'user1'; $authPlugin = new DAV\Auth\Plugin($authBackend, 'SabreDAV'); $this->server->addPlugin($authPlugin); // This forces a login $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); $this->response = new HTTP\ResponseMock(); $this->server->httpResponse = $this->response; } function testSimple() { $this->assertEquals(array('MKCALENDAR'), $this->plugin->getHTTPMethods('calendars/user1/randomnewcalendar')); $this->assertEquals(array('calendar-access','calendar-proxy'), $this->plugin->getFeatures()); $this->assertArrayHasKey('urn:ietf:params:xml:ns:caldav', $this->server->xmlNamespaces); } function testUnknownMethodPassThrough() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'MKBREAKFAST', 'REQUEST_URI' => '/', )); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(501, $this->response->status,'Incorrect status returned. Full response body:' . $this->response->body); } function testReportPassThrough() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/', )); $request->setBody(''); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(415, $this->response->status); } function testMkCalendarBadLocation() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'MKCALENDAR', 'REQUEST_URI' => '/blabla', )); $body = ' Lisa\'s Events Calendar restricted to events. '; $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(403, $this->response->status); } function testMkCalendarNoParentNode() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'MKCALENDAR', 'REQUEST_URI' => '/doesntexist/calendar', )); $body = ' Lisa\'s Events Calendar restricted to events. '; $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(409, $this->response->status); } function testMkCalendarExistingCalendar() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'MKCALENDAR', 'REQUEST_URI' => '/calendars/user1/UUID-123467', )); $body = ' Lisa\'s Events Calendar restricted to events. '; $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(405, $this->response->status); } function testMkCalendarSucceed() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'MKCALENDAR', 'REQUEST_URI' => '/calendars/user1/NEWCALENDAR', )); $timezone = 'BEGIN:VCALENDAR PRODID:-//Example Corp.//CalDAV Client//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:US-Eastern LAST-MODIFIED:19870101T000000Z BEGIN:STANDARD DTSTART:19671029T020000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 TZOFFSETFROM:-0400 TZOFFSETTO:-0500 TZNAME:Eastern Standard Time (US & Canada) END:STANDARD BEGIN:DAYLIGHT DTSTART:19870405T020000 RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 TZOFFSETFROM:-0500 TZOFFSETTO:-0400 TZNAME:Eastern Daylight Time (US & Canada) END:DAYLIGHT END:VTIMEZONE END:VCALENDAR'; $body = ' Lisa\'s Events Calendar restricted to events. '; $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(201, $this->response->status,'Invalid response code received. Full response body: ' .$this->response->body); $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); $this->assertEquals(3, count($calendars)); $newCalendar = null; foreach($calendars as $calendar) { if ($calendar['uri'] === 'NEWCALENDAR') { $newCalendar = $calendar; break; } } $this->assertInternalType('array',$newCalendar); $keys = array( 'uri' => 'NEWCALENDAR', 'id' => null, '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar restricted to events.', '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => $timezone, '{DAV:}displayname' => 'Lisa\'s Events', '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null, ); foreach($keys as $key=>$value) { $this->assertArrayHasKey($key, $newCalendar); if (is_null($value)) continue; $this->assertEquals($value, $newCalendar[$key]); } $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; $this->assertTrue($newCalendar[$sccs] instanceof Property\SupportedCalendarComponentSet); $this->assertEquals(array('VEVENT'),$newCalendar[$sccs]->getValue()); } function testMkCalendarEmptyBodySucceed() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'MKCALENDAR', 'REQUEST_URI' => '/calendars/user1/NEWCALENDAR', )); $request->setBody(''); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(201, $this->response->status,'Invalid response code received. Full response body: ' .$this->response->body); $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); $this->assertEquals(3, count($calendars)); $newCalendar = null; foreach($calendars as $calendar) { if ($calendar['uri'] === 'NEWCALENDAR') { $newCalendar = $calendar; break; } } $this->assertInternalType('array',$newCalendar); $keys = array( 'uri' => 'NEWCALENDAR', 'id' => null, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null, ); foreach($keys as $key=>$value) { $this->assertArrayHasKey($key, $newCalendar); if (is_null($value)) continue; $this->assertEquals($value, $newCalendar[$key]); } $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; $this->assertTrue($newCalendar[$sccs] instanceof Property\SupportedCalendarComponentSet); $this->assertEquals(array('VEVENT','VTODO'),$newCalendar[$sccs]->getValue()); } function testPrincipalProperties() { $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_HOST' => 'sabredav.org', )); $this->server->httpRequest = $httpRequest; $props = $this->server->getPropertiesForPath('/principals/user1',array( '{urn:ietf:params:xml:ns:caldav}calendar-home-set', '{' . Plugin::NS_CALENDARSERVER . '}calendar-proxy-read-for', '{' . Plugin::NS_CALENDARSERVER . '}calendar-proxy-write-for', '{' . Plugin::NS_CALENDARSERVER . '}notification-URL', )); $this->assertArrayHasKey(0,$props); $this->assertArrayHasKey(200,$props[0]); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-home-set',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-home-set']; $this->assertTrue($prop instanceof DAV\Property\Href); $this->assertEquals('calendars/user1/',$prop->getHref()); $this->assertArrayHasKey('{http://calendarserver.org/ns/}calendar-proxy-read-for', $props[0][200]); $prop = $props[0][200]['{http://calendarserver.org/ns/}calendar-proxy-read-for']; $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $prop); $this->assertEquals(array('principals/admin'), $prop->getHrefs()); $this->assertArrayHasKey('{http://calendarserver.org/ns/}calendar-proxy-write-for', $props[0][200]); $prop = $props[0][200]['{http://calendarserver.org/ns/}calendar-proxy-write-for']; $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $prop); $this->assertEquals(array('principals/admin'), $prop->getHrefs()); } function testSupportedReportSetPropertyNonCalendar() { $props = $this->server->getPropertiesForPath('/calendars/user1',array( '{DAV:}supported-report-set', )); $this->assertArrayHasKey(0,$props); $this->assertArrayHasKey(200,$props[0]); $this->assertArrayHasKey('{DAV:}supported-report-set',$props[0][200]); $prop = $props[0][200]['{DAV:}supported-report-set']; $this->assertInstanceOf('\\Sabre\\DAV\\Property\\SupportedReportSet', $prop); $value = array( '{DAV:}expand-property', '{DAV:}principal-property-search', '{DAV:}principal-search-property-set' ); $this->assertEquals($value,$prop->getValue()); } /** * @depends testSupportedReportSetPropertyNonCalendar */ function testSupportedReportSetProperty() { $props = $this->server->getPropertiesForPath('/calendars/user1/UUID-123467',array( '{DAV:}supported-report-set', )); $this->assertArrayHasKey(0,$props); $this->assertArrayHasKey(200,$props[0]); $this->assertArrayHasKey('{DAV:}supported-report-set',$props[0][200]); $prop = $props[0][200]['{DAV:}supported-report-set']; $this->assertTrue($prop instanceof \Sabre\DAV\Property\SupportedReportSet); $value = array( '{urn:ietf:params:xml:ns:caldav}calendar-multiget', '{urn:ietf:params:xml:ns:caldav}calendar-query', '{urn:ietf:params:xml:ns:caldav}free-busy-query', '{DAV:}expand-property', '{DAV:}principal-property-search', '{DAV:}principal-search-property-set' ); $this->assertEquals($value,$prop->getValue()); } function testSupportedReportSetUserCalendars() { $this->server->addPlugin(new \Sabre\DAV\Sync\Plugin()); $props = $this->server->getPropertiesForPath('/calendars/user1',array( '{DAV:}supported-report-set', )); $this->assertArrayHasKey(0,$props); $this->assertArrayHasKey(200,$props[0]); $this->assertArrayHasKey('{DAV:}supported-report-set',$props[0][200]); $prop = $props[0][200]['{DAV:}supported-report-set']; $this->assertTrue($prop instanceof \Sabre\DAV\Property\SupportedReportSet); $value = array( '{DAV:}sync-collection', '{DAV:}expand-property', '{DAV:}principal-property-search', '{DAV:}principal-search-property-set', ); $this->assertEquals($value,$prop->getValue()); } /** * @depends testSupportedReportSetProperty */ function testCalendarMultiGetReport() { $body = '' . '' . '' . ' ' . ' ' . '' . '/calendars/user1/UUID-123467/UUID-2345' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1', 'HTTP_DEPTH' => '1', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(207, $this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body)); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $check = array( '/d:multistatus', '/d:multistatus/d:response', '/d:multistatus/d:response/d:href', '/d:multistatus/d:response/d:propstat', '/d:multistatus/d:response/d:propstat/d:prop', '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data', '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', ); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $this->assertEquals(1,count($result)); if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); } // The response object should have a reference to the Asia/Seoul // timezone. $this->assertTrue(strpos($this->response->body,'Asia/Seoul')!==false); } /** * @depends testCalendarMultiGetReport */ function testCalendarMultiGetReportExpand() { $body = '' . '' . '' . ' ' . ' ' . ' ' . ' ' . '' . '/calendars/user1/UUID-123467/UUID-2345' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1', 'HTTP_DEPTH' => '1', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(207, $this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body)); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $check = array( '/d:multistatus', '/d:multistatus/d:response', '/d:multistatus/d:response/d:href', '/d:multistatus/d:response/d:propstat', '/d:multistatus/d:response/d:propstat/d:prop', '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data', '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', ); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $this->assertEquals(1,count($result)); if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); } // The response object should no longer hold references to timezones. $this->assertTrue(strpos($this->response->body,'Asia/Seoul')===false); } /** * @depends testSupportedReportSetProperty * @depends testCalendarMultiGetReport */ function testCalendarQueryReport() { $body = '' . '' . '' . ' ' . ' ' . ' ' . ' ' . '' . '' . ' ' . ' ' . ' ' . '' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1/UUID-123467', 'HTTP_DEPTH' => '1', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(207, $this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body)); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $check = array( '/d:multistatus', '/d:multistatus/d:response', '/d:multistatus/d:response/d:href', '/d:multistatus/d:response/d:propstat', '/d:multistatus/d:response/d:propstat/d:prop', '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data', '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', ); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body); if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); } } /** * @depends testSupportedReportSetProperty * @depends testCalendarMultiGetReport */ function testCalendarQueryReportWindowsPhone() { $body = '' . '' . '' . ' ' . ' ' . ' ' . ' ' . '' . '' . ' ' . ' ' . ' ' . '' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1/UUID-123467', 'HTTP_USER_AGENT' => 'MSFT-WP/8.10.14219 (gzip)', 'HTTP_DEPTH' => '0', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(207, $this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $expectedIcal = TestUtil::getTestCalendarData(); $expectedIcal = \Sabre\VObject\Reader::read($expectedIcal); $expectedIcal->expand( new \DateTime('2000-01-01 00:00:00', new \DateTimeZone('UTC')), new \DateTime('2010-12-31 23:59:59', new \DateTimeZone('UTC')) ); $expectedIcal = str_replace("\r\n", " \n", $expectedIcal->serialize()); $expected = << /calendars/user1/UUID-123467/UUID-2345 $expectedIcal "e207e33c10e5fb9c12cfb35b5d9116e1" HTTP/1.1 200 OK XML; $this->assertXmlStringEqualsXmlString($expected, $this->response->getBodyAsString()); } /** * @depends testSupportedReportSetProperty * @depends testCalendarMultiGetReport */ function testCalendarQueryReportBadDepth() { $body = '' . '' . '' . ' ' . ' ' . ' ' . ' ' . '' . '' . ' ' . ' ' . ' ' . '' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1/UUID-123467', 'HTTP_DEPTH' => '0', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(400, $this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); } /** * @depends testCalendarQueryReport */ function testCalendarQueryReportNoCalData() { $body = '' . '' . '' . ' ' . '' . '' . ' ' . ' ' . ' ' . '' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1//UUID-123467', 'HTTP_DEPTH' => '1', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(207, $this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body)); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $check = array( '/d:multistatus', '/d:multistatus/d:response', '/d:multistatus/d:response/d:href', '/d:multistatus/d:response/d:propstat', '/d:multistatus/d:response/d:propstat/d:prop', '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', ); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body); if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); } } /** * @depends testCalendarQueryReport */ function testCalendarQueryReportNoFilters() { $body = '' . '' . '' . ' ' . ' ' . '' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1//UUID-123467', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(400, $this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); } /** * @depends testSupportedReportSetProperty * @depends testCalendarMultiGetReport */ function testCalendarQueryReport1Object() { $body = '' . '' . '' . ' ' . ' ' . ' ' . ' ' . '' . '' . ' ' . ' ' . ' ' . '' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1/UUID-123467/UUID-2345', 'HTTP_DEPTH' => '0', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(207, $this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body)); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $check = array( '/d:multistatus', '/d:multistatus/d:response', '/d:multistatus/d:response/d:href', '/d:multistatus/d:response/d:propstat', '/d:multistatus/d:response/d:propstat/d:prop', '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', '/d:multistatus/d:response/d:propstat/d:prop/c:calendar-data', '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', ); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body); if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); } } /** * @depends testSupportedReportSetProperty * @depends testCalendarMultiGetReport */ function testCalendarQueryReport1ObjectNoCalData() { $body = '' . '' . '' . ' ' . '' . '' . ' ' . ' ' . ' ' . '' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1/UUID-123467/UUID-2345', 'HTTP_DEPTH' => '0', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(207, $this->response->status,'Received an unexpected status. Full response body: ' . $this->response->body); $xml = simplexml_load_string(DAV\XMLUtil::convertDAVNamespace($this->response->body)); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:caldav'); $check = array( '/d:multistatus', '/d:multistatus/d:response', '/d:multistatus/d:response/d:href', '/d:multistatus/d:response/d:propstat', '/d:multistatus/d:response/d:propstat/d:prop', '/d:multistatus/d:response/d:propstat/d:prop/d:getetag', '/d:multistatus/d:response/d:propstat/d:status' => 'HTTP/1.1 200 OK', ); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $this->assertEquals(1,count($result), 'We expected 1 ' . $xpath . ' elements. We\'ve found ' . count($result) . '. Full result: ' . $this->response->body); if (!is_int($v1)) $this->assertEquals($v2,(string)$result[0]); } } function testHTMLActionsPanel() { $output = ''; $r = $this->server->emit('onHTMLActionsPanel', [$this->server->tree->getNodeForPath('calendars/user1'), &$output]); $this->assertFalse($r); $this->assertTrue(!!strpos($output,'Display name')); } function testBrowserPostAction() { $r = $this->server->emit('onBrowserPostAction', ['calendars/user1', 'mkcalendar', [ 'name' => 'NEWCALENDAR', '{DAV:}displayname' => 'foo', ]]); $this->assertFalse($r); $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); $this->assertEquals(3, count($calendars)); $newCalendar = null; foreach($calendars as $calendar) { if ($calendar['uri'] === 'NEWCALENDAR') { $newCalendar = $calendar; break; } } if (!$newCalendar) $this->fail('Could not find newly created calendar'); } /** * @depends testCalendarMultiGetReport */ function testCalendarMultiGetReportNoEnd() { $body = '' . '' . '' . ' ' . ' ' . ' ' . ' ' . '' . '/calendars/user1/UUID-123467/UUID-2345' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1', 'HTTP_DEPTH' => '1', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(400, $this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); } /** * @depends testCalendarMultiGetReport */ function testCalendarMultiGetReportNoStart() { $body = '' . '' . '' . ' ' . ' ' . ' ' . ' ' . '' . '/calendars/user1/UUID-123467/UUID-2345' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1', 'HTTP_DEPTH' => '1', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(400, $this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); } /** * @depends testCalendarMultiGetReport */ function testCalendarMultiGetReportEndBeforeStart() { $body = '' . '' . '' . ' ' . ' ' . ' ' . ' ' . '' . '/calendars/user1/UUID-123467/UUID-2345' . ''; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/calendars/user1', 'HTTP_DEPTH' => '1', )); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(400, $this->response->status,'Invalid HTTP status received. Full response body: ' . $this->response->body); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Principal/000077500000000000000000000000001267035675400206275ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Principal/CollectionTest.php000066400000000000000000000006621267035675400242770ustar00rootroot00000000000000getChildForPrincipal(array( 'uri' => 'principals/admin', )); $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\User', $r); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Principal/ProxyReadTest.php000066400000000000000000000040501267035675400241140ustar00rootroot00000000000000 'principal/user', )); $this->backend = $backend; return $principal; } function testGetName() { $i = $this->getInstance(); $this->assertEquals('calendar-proxy-read', $i->getName()); } function testGetDisplayName() { $i = $this->getInstance(); $this->assertEquals('calendar-proxy-read', $i->getDisplayName()); } function testGetLastModified() { $i = $this->getInstance(); $this->assertNull($i->getLastModified()); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ function testDelete() { $i = $this->getInstance(); $i->delete(); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ function testSetName() { $i = $this->getInstance(); $i->setName('foo'); } function testGetAlternateUriSet() { $i = $this->getInstance(); $this->assertEquals(array(), $i->getAlternateUriSet()); } function testGetPrincipalUri() { $i = $this->getInstance(); $this->assertEquals('principal/user/calendar-proxy-read', $i->getPrincipalUrl()); } function testGetGroupMemberSet() { $i = $this->getInstance(); $this->assertEquals(array(), $i->getGroupMemberSet()); } function testGetGroupMembership() { $i = $this->getInstance(); $this->assertEquals(array(), $i->getGroupMembership()); } function testSetGroupMemberSet() { $i = $this->getInstance(); $i->setGroupMemberSet(array('principals/foo')); $expected = array( $i->getPrincipalUrl() => array('principals/foo') ); $this->assertEquals($expected, $this->backend->groupMembers); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Principal/ProxyWriteTest.php000066400000000000000000000015021267035675400243320ustar00rootroot00000000000000 'principal/user', )); $this->backend = $backend; return $principal; } function testGetName() { $i = $this->getInstance(); $this->assertEquals('calendar-proxy-write', $i->getName()); } function testGetDisplayName() { $i = $this->getInstance(); $this->assertEquals('calendar-proxy-write', $i->getDisplayName()); } function testGetPrincipalUri() { $i = $this->getInstance(); $this->assertEquals('principal/user/calendar-proxy-write', $i->getPrincipalUrl()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Principal/UserTest.php000066400000000000000000000060631267035675400231230ustar00rootroot00000000000000addPrincipal(array( 'uri' => 'principals/user/calendar-proxy-read', )); $backend->addPrincipal(array( 'uri' => 'principals/user/calendar-proxy-write', )); $backend->addPrincipal(array( 'uri' => 'principals/user/random', )); return new User($backend, array( 'uri' => 'principals/user', )); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ function testCreateFile() { $u = $this->getInstance(); $u->createFile('test'); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ function testCreateDirectory() { $u = $this->getInstance(); $u->createDirectory('test'); } function testGetChildProxyRead() { $u = $this->getInstance(); $child = $u->getChild('calendar-proxy-read'); $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\ProxyRead', $child); } function testGetChildProxyWrite() { $u = $this->getInstance(); $child = $u->getChild('calendar-proxy-write'); $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\ProxyWrite', $child); } /** * @expectedException Sabre\DAV\Exception\NotFound */ function testGetChildNotFound() { $u = $this->getInstance(); $child = $u->getChild('foo'); } /** * @expectedException Sabre\DAV\Exception\NotFound */ function testGetChildNotFound2() { $u = $this->getInstance(); $child = $u->getChild('random'); } function testGetChildren() { $u = $this->getInstance(); $children = $u->getChildren(); $this->assertEquals(2, count($children)); $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\ProxyRead', $children[0]); $this->assertInstanceOf('Sabre\\CalDAV\\Principal\\ProxyWrite', $children[1]); } function testChildExist() { $u = $this->getInstance(); $this->assertTrue($u->childExists('calendar-proxy-read')); $this->assertTrue($u->childExists('calendar-proxy-write')); $this->assertFalse($u->childExists('foo')); } function testGetACL() { $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => '{DAV:}authenticated', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user/calendar-proxy-read', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user/calendar-proxy-write', 'protected' => true, ), ); $u = $this->getInstance(); $this->assertEquals($expected, $u->getACL()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Property/000077500000000000000000000000001267035675400205325ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Property/AllowedSharingModesTest.php000066400000000000000000000021521267035675400257760ustar00rootroot00000000000000assertInstanceOf('Sabre\CalDAV\Property\AllowedSharingModes', $sccs); } /** * @depends testSimple */ function testSerialize() { $property = new AllowedSharingModes(true,true); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV); $root->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' ' . '' . '' . ' ', $xml); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Property/EmailAddressSetTest.php000066400000000000000000000020411267035675400251110ustar00rootroot00000000000000assertEquals(['foo@example.org'], $eas->getValue()); } /** * @depends testSimple */ function testSerialize() { $property = new EmailAddressSet(['foo@example.org']); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $root->setAttribute('xmlns:cs',\Sabre\CalDAV\Plugin::NS_CALENDARSERVER); $doc->appendChild($root); $server = new \Sabre\DAV\Server(); $server->addPlugin(new \Sabre\CalDAV\Plugin()); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' ' . 'foo@example.org' . ' ', $xml); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Property/InviteTest.php000066400000000000000000000131461267035675400233460ustar00rootroot00000000000000assertInstanceOf('Sabre\CalDAV\Property\Invite', $sccs); } /** * @depends testSimple */ function testSerialize() { $property = new Invite(array( array( 'href' => 'mailto:user1@example.org', 'status' => CalDAV\SharingPlugin::STATUS_ACCEPTED, 'readOnly' => false, ), array( 'href' => 'mailto:user2@example.org', 'commonName' => 'John Doe', 'status' => CalDAV\SharingPlugin::STATUS_DECLINED, 'readOnly' => true, ), array( 'href' => 'mailto:user3@example.org', 'commonName' => 'Joe Shmoe', 'status' => CalDAV\SharingPlugin::STATUS_NORESPONSE, 'readOnly' => true, 'summary' => 'Something, something', ), array( 'href' => 'mailto:user4@example.org', 'commonName' => 'Hoe Boe', 'status' => CalDAV\SharingPlugin::STATUS_INVALID, 'readOnly' => true, ), ), array( 'href' => 'mailto:thedoctor@example.org', 'commonName' => 'The Doctor', 'firstName' => 'The', 'lastName' => 'Doctor', )); $doc = new \DOMDocument(); $doc->formatOutput = true; $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV); $root->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' mailto:thedoctor@example.org The Doctor The Doctor mailto:user1@example.org mailto:user2@example.org John Doe mailto:user3@example.org Joe Shmoe Something, something mailto:user4@example.org Hoe Boe ', $xml); } /** * @depends testSerialize */ public function testUnserialize() { $input = array( array( 'href' => 'mailto:user1@example.org', 'status' => CalDAV\SharingPlugin::STATUS_ACCEPTED, 'readOnly' => false, 'commonName' => '', 'summary' => '', ), array( 'href' => 'mailto:user2@example.org', 'commonName' => 'John Doe', 'status' => CalDAV\SharingPlugin::STATUS_DECLINED, 'readOnly' => true, 'summary' => '', ), array( 'href' => 'mailto:user3@example.org', 'commonName' => 'Joe Shmoe', 'status' => CalDAV\SharingPlugin::STATUS_NORESPONSE, 'readOnly' => true, 'summary' => 'Something, something', ), array( 'href' => 'mailto:user4@example.org', 'commonName' => 'Hoe Boe', 'status' => CalDAV\SharingPlugin::STATUS_INVALID, 'readOnly' => true, 'summary' => '', ), ); // Creating the xml $doc = new \DOMDocument(); $doc->formatOutput = true; $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV); $root->setAttribute('xmlns:cs',CalDAV\Plugin::NS_CALENDARSERVER); $doc->appendChild($root); $server = new DAV\Server(); $inputProperty = new Invite($input); $inputProperty->serialize($server, $root); $xml = $doc->saveXML(); // Parsing it again $doc2 = DAV\XMLUtil::loadDOMDocument($xml); $outputProperty = Invite::unserialize($doc2->firstChild, array()); $this->assertEquals($input, $outputProperty->getValue()); } /** * @expectedException Sabre\DAV\Exception */ function testUnserializeNoStatus() { $xml = ' mailto:user1@example.org '; $doc2 = DAV\XMLUtil::loadDOMDocument($xml); $outputProperty = Invite::unserialize($doc2->firstChild, array()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Property/ScheduleCalendarTranspTest.php000066400000000000000000000042221267035675400264610ustar00rootroot00000000000000assertEquals('transparent', $sccs->getValue()); } /** * @expectedException InvalidArgumentException */ function testBadArg() { $sccs = new ScheduleCalendarTransp('foo'); } function values() { return array( array('transparent'), array('opaque'), ); } /** * @depends testSimple * @dataProvider values */ function testSerialize($value) { $property = new ScheduleCalendarTransp($value); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' ' . '' . ' ', $xml); } /** * @depends testSimple * @dataProvider values */ function testUnserializer($value) { $xml = ' ' . '' . ''; $dom = DAV\XMLUtil::loadDOMDocument($xml); $property = ScheduleCalendarTransp::unserialize($dom->firstChild, array()); $this->assertTrue($property instanceof ScheduleCalendarTransp); $this->assertEquals($value, $property->getValue()); } /** * @depends testSimple */ function testUnserializerBadData() { $xml = ' ' . '' . ''; $dom = DAV\XMLUtil::loadDOMDocument($xml); $this->assertNull(ScheduleCalendarTransp::unserialize($dom->firstChild, array())); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Property/SupportedCalendarComponentSetTest.php000066400000000000000000000032121267035675400300570ustar00rootroot00000000000000assertEquals(array('VEVENT'), $sccs->getValue()); } /** * @depends testSimple */ function testSerialize() { $property = new SupportedCalendarComponentSet(array('VEVENT','VJOURNAL')); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $root->setAttribute('xmlns:cal',\Sabre\CalDAV\Plugin::NS_CALDAV); $doc->appendChild($root); $server = new \Sabre\DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' ' . '' . '' . ' ', $xml); } /** * @depends testSimple */ function testUnserializer() { $xml = ' ' . '' . '' . ''; $dom = \Sabre\DAV\XMLUtil::loadDOMDocument($xml); $property = SupportedCalendarComponentSet::unserialize($dom->firstChild, array()); $this->assertTrue($property instanceof SupportedCalendarComponentSet); $this->assertEquals(array( 'VEVENT', 'VJOURNAL', ), $property->getValue()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Property/SupportedCalendarDataTest.php000066400000000000000000000020651267035675400263170ustar00rootroot00000000000000assertInstanceOf('Sabre\CalDAV\Property\SupportedCalendarData', $sccs); } /** * @depends testSimple */ function testSerialize() { $property = new SupportedCalendarData(); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' ' . '' . '' . ' ', $xml); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Property/SupportedCollationSetTest.php000066400000000000000000000021741267035675400264150ustar00rootroot00000000000000assertInstanceOf('Sabre\CalDAV\Property\SupportedCollationSet', $scs); } /** * @depends testSimple */ function testSerialize() { $property = new SupportedCollationSet(); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $root->setAttribute('xmlns:cal',CalDAV\Plugin::NS_CALDAV); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' ' . 'i;ascii-casemap' . 'i;octet' . 'i;unicode-casemap' . ' ', $xml); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/000077500000000000000000000000001267035675400204425ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/DeliverNewEventTest.php000066400000000000000000000052111267035675400250600ustar00rootroot00000000000000caldavBackend->createCalendar( 'principals/user1', 'default', [ ] ); $this->caldavBackend->createCalendar( 'principals/user2', 'default', [ ] ); } function testDelivery() { $request = new Request('PUT', '/calendars/user1/default/foo.ics'); $request->setBody(<<server->on('schedule', function($message) use (&$messages) { $messages[] = $message; }); $response = $this->request($request); $this->assertEquals(201, $response->getStatus(), 'Incorrect status code received. Response body:' . $response->getBodyAsString()); $result = $this->request(new Request('GET', '/calendars/user1/default/foo.ics'))->getBody(); $resultVObj = VObject\Reader::read($result); $this->assertEquals( '1.2', $resultVObj->VEVENT->ATTENDEE[1]['SCHEDULE-STATUS']->getValue() ); $this->assertEquals(1, count($messages)); $message = $messages[0]; $this->assertInstanceOf('\Sabre\VObject\ITip\Message', $message); $this->assertEquals('mailto:user2.sabredav@sabredav.org', $message->recipient); $this->assertEquals('Roxy Kesh', $message->recipientName); $this->assertEquals('mailto:user1.sabredav@sabredav.org', $message->sender); $this->assertEquals('Administrator', $message->senderName); $this->assertEquals('REQUEST', $message->method); $this->assertEquals('REQUEST', $message->message->METHOD->getValue()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/FreeBusyRequestTest.php000066400000000000000000000267221267035675400251210ustar00rootroot00000000000000 'principals/user2', 'id' => 1, 'uri' => 'calendar1', '{' . CalDAV\Plugin::NS_CALDAV . '}calendar-timezone' => "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nEND:VTIMEZONE\r\nEND:VCALENDAR", ), array( 'principaluri' => 'principals/user2', 'id' => 2, 'uri' => 'calendar2', '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp(ScheduleCalendarTransp::TRANSPARENT), ), ); $calendarobjects = array( 1 => array( '1.ics' => array( 'uri' => '1.ics', 'calendardata' => 'BEGIN:VCALENDAR BEGIN:VEVENT DTSTART:20110101T130000 DURATION:PT1H END:VEVENT END:VCALENDAR', 'calendarid' => 1, )), 2 => array( '2.ics' => array( 'uri' => '2.ics', 'calendardata' => 'BEGIN:VCALENDAR BEGIN:VEVENT DTSTART:20110101T080000 DURATION:PT1H END:VEVENT END:VCALENDAR', 'calendarid' => 2, )) ); $principalBackend = new DAVACL\PrincipalBackend\Mock(); $this->caldavBackend = new CalDAV\Backend\MockScheduling($calendars, $calendarobjects); $tree = array( new DAVACL\PrincipalCollection($principalBackend), new CalDAV\CalendarRoot($principalBackend, $this->caldavBackend), ); $this->request = HTTP\Sapi::createFromServerArray([ 'CONTENT_TYPE' => 'text/calendar', ]); $this->response = new HTTP\ResponseMock(); $this->server = new DAV\Server($tree); $this->server->httpRequest = $this->request; $this->server->httpResponse = $this->response; $this->aclPlugin = new DAVACL\Plugin(); $this->server->addPlugin($this->aclPlugin); $authBackend = new DAV\Auth\Backend\Mock(); $authBackend->setCurrentUser('user1'); $this->authPlugin = new DAV\Auth\Plugin($authBackend,'SabreDAV'); $this->server->addPlugin($this->authPlugin); // CalDAV plugin $this->plugin = new CalDAV\Plugin(); $this->server->addPlugin($this->plugin); // Scheduling plugin $this->plugin = new Plugin(); $this->server->addPlugin($this->plugin); } function testWrongContentType() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/outbox', ['Content-Type' => 'text/plain'] ); $this->assertNull( $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse) ); } function testNotFound() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/blabla', ['Content-Type' => 'text/calendar'] ); $this->assertNull( $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse) ); } function testNotOutbox() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/inbox', ['Content-Type' => 'text/calendar'] ); $this->assertNull( $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse) ); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testNoItipMethod() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/outbox', ['Content-Type' => 'text/calendar'] ); $body = <<server->httpRequest->setBody($body); $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); } /** * @expectedException \Sabre\DAV\Exception\NotImplemented */ function testNoVFreeBusy() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/outbox', ['Content-Type' => 'text/calendar'] ); $body = <<server->httpRequest->setBody($body); $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ function testIncorrectOrganizer() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/outbox', ['Content-Type' => 'text/calendar'] ); $body = <<server->httpRequest->setBody($body); $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testNoAttendees() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/outbox', ['Content-Type' => 'text/calendar'] ); $body = <<server->httpRequest->setBody($body); $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testNoDTStart() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/outbox', ['Content-Type' => 'text/calendar'] ); $body = <<server->httpRequest->setBody($body); $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse); } function testSucceed() { $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/outbox', ['Content-Type' => 'text/calendar'] ); $body = <<server->httpRequest->setBody($body); // Lazily making the current principal an admin. $this->aclPlugin->adminPrincipals[] = 'principals/user1'; $this->assertFalse( $this->plugin->httpPost($this->server->httpRequest, $this->response) ); $this->assertEquals(200, $this->response->status); $this->assertEquals(array( 'Content-Type' => ['application/xml'], ), $this->response->getHeaders()); $strings = array( 'mailto:user2.sabredav@sabredav.org', 'mailto:user3.sabredav@sabredav.org', '2.0;Success', '3.7;Could not find principal', 'FREEBUSY;FBTYPE=BUSY:20110101T120000Z/20110101T130000Z', ); foreach($strings as $string) { $this->assertTrue( strpos($this->response->body, $string)!==false, 'The response body did not contain: ' . $string .'Full response: ' . $this->response->body ); } $this->assertTrue( strpos($this->response->body, 'FREEBUSY;FBTYPE=BUSY:20110101T080000Z/20110101T090000Z')==false, 'The response body did contain free busy info from a transparent calendar.' ); } /** * Testing if the freebusy request still works, even if there are no * calendars in the target users' account. */ function testSucceedNoCalendars() { // Deleting calendars $this->caldavBackend->deleteCalendar(1); $this->caldavBackend->deleteCalendar(2); $this->server->httpRequest = new HTTP\Request( 'POST', '/calendars/user1/outbox', ['Content-Type' => 'text/calendar'] ); $body = <<server->httpRequest->setBody($body); // Lazily making the current principal an admin. $this->aclPlugin->adminPrincipals[] = 'principals/user1'; $this->assertFalse( $this->plugin->httpPost($this->server->httpRequest, $this->response) ); $this->assertEquals(200, $this->response->status); $this->assertEquals(array( 'Content-Type' => ['application/xml'], ), $this->response->getHeaders()); $strings = array( 'mailto:user2.sabredav@sabredav.org', '2.0;Success', ); foreach($strings as $string) { $this->assertTrue( strpos($this->response->body, $string)!==false, 'The response body did not contain: ' . $string .'Full response: ' . $this->response->body ); } } /* function testNoPrivilege() { $this->markTestIncomplete('Currently there\'s no "no privilege" situation'); $this->server->httpRequest = HTTP\Sapi::createFromServerArray(array( 'CONTENT_TYPE' => 'text/calendar', 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/outbox', )); $body = <<server->httpRequest->setBody($body); $this->assertFalse( $this->plugin->httpPost($this->server->httpRequest, $this->response) ); $this->assertEquals(200, $this->response->status); $this->assertEquals([ 'Content-Type' => 'application/xml', ], $this->response->getHeaders()); $strings = [ 'mailto:user2.sabredav@sabredav.org', '3.7;No calendar-home-set property found', ]; foreach($strings as $string) { $this->assertTrue( strpos($this->response->body, $string)!==false, 'The response body did not contain: ' . $string .'Full response: ' . $this->response->body ); } }*/ } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/IMip/000077500000000000000000000000001267035675400213005ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/IMip/MockPlugin.php000066400000000000000000000023641267035675400240660ustar00rootroot00000000000000emails[] = array( 'to' => $to, 'subject' => $subject, 'body' => $body, 'headers' => $headers, ); } public function getSentEmails() { return $this->emails; } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/IMipPluginTest.php000066400000000000000000000122241267035675400240310ustar00rootroot00000000000000sender = 'mailto:sender@example.org'; $message->senderName = 'Sender'; $message->recipient = 'mailto:recipient@example.org'; $message->recipientName = 'Recipient'; $message->method = 'REPLY'; $ics = <<message = Reader::read($ics); $result = $this->schedule($message); $expected = [ [ 'to' => 'Recipient ', 'subject' => 'Re: Birthday party', 'body' => $ics, 'headers' => [ 'Reply-To: Sender ', 'From: system@example.org', 'Content-Type: text/calendar; charset=UTF-8; method=REPLY', 'X-Sabre-Version: ' . \Sabre\DAV\Version::VERSION, ], ] ]; $this->assertEquals($expected, $result); } function testDeliverReplyNoMailto() { $message = new Message(); $message->sender = 'mailto:sender@example.org'; $message->senderName = 'Sender'; $message->recipient = 'http://example.org/recipient'; $message->recipientName = 'Recipient'; $message->method = 'REPLY'; $ics = <<message = Reader::read($ics); $result = $this->schedule($message); $expected = []; $this->assertEquals($expected, $result); } function testDeliverRequest() { $message = new Message(); $message->sender = 'mailto:sender@example.org'; $message->senderName = 'Sender'; $message->recipient = 'mailto:recipient@example.org'; $message->recipientName = 'Recipient'; $message->method = 'REQUEST'; $ics = <<message = Reader::read($ics); $result = $this->schedule($message); $expected = [ [ 'to' => 'Recipient ', 'subject' => 'Birthday party', 'body' => $ics, 'headers' => [ 'Reply-To: Sender ', 'From: system@example.org', 'Content-Type: text/calendar; charset=UTF-8; method=REQUEST', 'X-Sabre-Version: ' . \Sabre\DAV\Version::VERSION, ], ] ]; $this->assertEquals($expected, $result); } function testDeliverCancel() { $message = new Message(); $message->sender = 'mailto:sender@example.org'; $message->senderName = 'Sender'; $message->recipient = 'mailto:recipient@example.org'; $message->recipientName = 'Recipient'; $message->method = 'CANCEL'; $ics = <<message = Reader::read($ics); $result = $this->schedule($message); $expected = [ [ 'to' => 'Recipient ', 'subject' => 'Cancelled: Birthday party', 'body' => $ics, 'headers' => [ 'Reply-To: Sender ', 'From: system@example.org', 'Content-Type: text/calendar; charset=UTF-8; method=CANCEL', 'X-Sabre-Version: ' . \Sabre\DAV\Version::VERSION, ], ] ]; $this->assertEquals($expected, $result); $this->assertEquals('1.1', substr($message->scheduleStatus, 0, 3)); } function schedule(Message $message) { $plugin = new IMip\MockPlugin('system@example.org'); $server = new Server(); $server->addPlugin($plugin); $server->emit('schedule', [$message]); return $plugin->getSentEmails(); } function testDeliverInsignificantRequest() { $message = new Message(); $message->sender = 'mailto:sender@example.org'; $message->senderName = 'Sender'; $message->recipient = 'mailto:recipient@example.org'; $message->recipientName = 'Recipient'; $message->method = 'REQUEST'; $message->significantChange = false; $ics = <<message = Reader::read($ics); $result = $this->schedule($message); $expected = []; $this->assertEquals($expected, $result); $this->assertEquals('1.0', $message->getScheduleStatus()[0]); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/InboxTest.php000066400000000000000000000125351267035675400231000ustar00rootroot00000000000000assertEquals('inbox', $inbox->getName()); $this->assertEquals(array(), $inbox->getChildren()); $this->assertEquals('principals/user1', $inbox->getOwner()); $this->assertEquals(null, $inbox->getGroup()); $this->assertEquals(array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write-properties', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}unbind', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}unbind', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{urn:ietf:params:xml:ns:caldav}schedule-deliver-invite', 'principal' => '{DAV:}authenticated', 'protected' => true, ), array( 'privilege' => '{urn:ietf:params:xml:ns:caldav}schedule-deliver-reply', 'principal' => '{DAV:}authenticated', 'protected' => true, ), ), $inbox->getACL()); $ok = false; try { $inbox->setACL(array()); } catch (DAV\Exception\MethodNotAllowed $e) { $ok = true; } if (!$ok) { $this->fail('Exception was not emitted'); } } function testGetSupportedPrivilegeSet() { $inbox = new Inbox( new CalDAV\Backend\MockScheduling(), 'principals/user1' ); $r = $inbox->getSupportedPrivilegeSet(); $ok = 0; foreach($r['aggregates'] as $priv) { if ($priv['privilege'] == '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver') { $ok++; foreach($priv['aggregates'] as $subpriv) { if ($subpriv['privilege'] == '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver-invite') { $ok++; } if ($subpriv['privilege'] == '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver-reply') { $ok++; } } } } $this->assertEquals(3, $ok, "We're missing one or more privileges"); } /** * @depends testSetup */ function testGetChildren() { $backend = new CalDAV\Backend\MockScheduling(); $inbox = new Inbox( $backend, 'principals/user1' ); $this->assertEquals( 0, count($inbox->getChildren()) ); $backend->createSchedulingObject('principals/user1', 'schedule1.ics', "BEGIN:VCALENDAR\r\nEND:VCALENDAR"); $this->assertEquals( 1, count($inbox->getChildren()) ); $this->assertInstanceOf('Sabre\CalDAV\Schedule\SchedulingObject', $inbox->getChildren()[0]); $this->assertEquals( 'schedule1.ics', $inbox->getChildren()[0]->getName() ); } /** * @depends testGetChildren */ function testCreateFile() { $backend = new CalDAV\Backend\MockScheduling(); $inbox = new Inbox( $backend, 'principals/user1' ); $this->assertEquals( 0, count($inbox->getChildren()) ); $inbox->createFile('schedule1.ics', "BEGIN:VCALENDAR\r\nEND:VCALENDAR"); $this->assertEquals( 1, count($inbox->getChildren()) ); $this->assertInstanceOf('Sabre\CalDAV\Schedule\SchedulingObject', $inbox->getChildren()[0]); $this->assertEquals( 'schedule1.ics', $inbox->getChildren()[0]->getName() ); } /** * @depends testSetup */ function testCalendarQuery() { $backend = new CalDAV\Backend\MockScheduling(); $inbox = new Inbox( $backend, 'principals/user1' ); $this->assertEquals( 0, count($inbox->getChildren()) ); $backend->createSchedulingObject('principals/user1', 'schedule1.ics', "BEGIN:VCALENDAR\r\nEND:VCALENDAR"); $this->assertEquals( ['schedule1.ics'], $inbox->calendarQuery([ 'name' => 'VCALENDAR', 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false ]) ); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/OutboxPostTest.php000066400000000000000000000067631267035675400241550ustar00rootroot00000000000000 'POST', 'REQUEST_URI' => '/notfound', 'HTTP_CONTENT_TYPE' => 'text/calendar', )); $this->assertHTTPStatus(501, $req); } function testPostPassThruNotTextCalendar() { $req = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/outbox', )); $this->assertHTTPStatus(501, $req); } function testPostPassThruNoOutBox() { $req = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars', 'HTTP_CONTENT_TYPE' => 'text/calendar', )); $this->assertHTTPStatus(501, $req); } function testInvalidIcalBody() { $req = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/outbox', 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', 'HTTP_RECIPIENT' => 'mailto:user2@example.org', 'HTTP_CONTENT_TYPE' => 'text/calendar', )); $req->setBody('foo'); $this->assertHTTPStatus(400, $req); } function testNoVEVENT() { $req = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/outbox', 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', 'HTTP_RECIPIENT' => 'mailto:user2@example.org', 'HTTP_CONTENT_TYPE' => 'text/calendar', )); $body = array( 'BEGIN:VCALENDAR', 'BEGIN:VTIMEZONE', 'END:VTIMEZONE', 'END:VCALENDAR', ); $req->setBody(implode("\r\n",$body)); $this->assertHTTPStatus(400, $req); } function testNoMETHOD() { $req = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/outbox', 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', 'HTTP_RECIPIENT' => 'mailto:user2@example.org', 'HTTP_CONTENT_TYPE' => 'text/calendar', )); $body = array( 'BEGIN:VCALENDAR', 'BEGIN:VEVENT', 'END:VEVENT', 'END:VCALENDAR', ); $req->setBody(implode("\r\n",$body)); $this->assertHTTPStatus(400, $req); } function testUnsupportedMethod() { $req = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/outbox', 'HTTP_ORIGINATOR' => 'mailto:user1.sabredav@sabredav.org', 'HTTP_RECIPIENT' => 'mailto:user2@example.org', 'HTTP_CONTENT_TYPE' => 'text/calendar', )); $body = array( 'BEGIN:VCALENDAR', 'METHOD:PUBLISH', 'BEGIN:VEVENT', 'END:VEVENT', 'END:VCALENDAR', ); $req->setBody(implode("\r\n",$body)); $this->assertHTTPStatus(501, $req); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/OutboxTest.php000066400000000000000000000053521267035675400233000ustar00rootroot00000000000000assertEquals('outbox', $outbox->getName()); $this->assertEquals(array(), $outbox->getChildren()); $this->assertEquals('principals/user1', $outbox->getOwner()); $this->assertEquals(null, $outbox->getGroup()); $this->assertEquals(array( array( 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), ), $outbox->getACL()); $ok = false; try { $outbox->setACL(array()); } catch (DAV\Exception\MethodNotAllowed $e) { $ok = true; } if (!$ok) { $this->fail('Exception was not emitted'); } } function testGetSupportedPrivilegeSet() { $outbox = new Outbox('principals/user1'); $r = $outbox->getSupportedPrivilegeSet(); $ok = 0; foreach($r['aggregates'] as $priv) { if ($priv['privilege'] == '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-query-freebusy') { $ok++; } if ($priv['privilege'] == '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-post-vevent') { $ok++; } } $this->assertEquals(2, $ok, "We're missing one or more privileges"); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/PluginBasicTest.php000066400000000000000000000012311267035675400242100ustar00rootroot00000000000000assertEquals(['calendar-auto-schedule'], $plugin->getFeatures()); } function testGetHTTPMethods() { $this->assertEquals([], $this->caldavSchedulePlugin->getHTTPMethods('notfound')); $this->assertEquals([], $this->caldavSchedulePlugin->getHTTPMethods('calendars/user1')); $this->assertEquals(['POST'], $this->caldavSchedulePlugin->getHTTPMethods('calendars/user1/outbox')); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/PluginPropertiesTest.php000066400000000000000000000050031267035675400253240ustar00rootroot00000000000000caldavBackend->createCalendar( 'principals/user1', 'default', [ ] ); } function testPrincipalProperties() { $props = $this->server->getPropertiesForPath('/principals/user1',array( '{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', '{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', '{urn:ietf:params:xml:ns:caldav}calendar-user-type', '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', )); $this->assertArrayHasKey(0,$props); $this->assertArrayHasKey(200,$props[0]); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL']; $this->assertTrue($prop instanceof DAV\Property\Href); $this->assertEquals('calendars/user1/outbox/',$prop->getHref()); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL']; $this->assertTrue($prop instanceof DAV\Property\Href); $this->assertEquals('calendars/user1/inbox/',$prop->getHref()); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set']; $this->assertTrue($prop instanceof DAV\Property\HrefList); $this->assertEquals(array('mailto:user1.sabredav@sabredav.org','/principals/user1/'),$prop->getHrefs()); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-type',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-type']; $this->assertEquals('INDIVIDUAL',$prop); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL']; $this->assertEquals('calendars/user1/default/',$prop->getHref()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/PluginPropertiesWithSharedCalendarTest.php000066400000000000000000000057131267035675400307510ustar00rootroot00000000000000caldavBackend->createCalendar( 'principals/user1', 'shared', [ '{http://calendarserver.org/ns/}shared-url' => new DAV\Property\Href('calendars/user2/default/'), '{http://sabredav.org/ns}read-only' => false, '{http://sabredav.org/ns}owner-principal' => 'principals/user2', ] ); $this->caldavBackend->createCalendar( 'principals/user1', 'default', [ ] ); } function testPrincipalProperties() { $props = $this->server->getPropertiesForPath('/principals/user1',array( '{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL', '{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL', '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set', '{urn:ietf:params:xml:ns:caldav}calendar-user-type', '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', )); $this->assertArrayHasKey(0,$props); $this->assertArrayHasKey(200,$props[0]); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL']; $this->assertTrue($prop instanceof DAV\Property\Href); $this->assertEquals('calendars/user1/outbox/',$prop->getHref()); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL']; $this->assertTrue($prop instanceof DAV\Property\Href); $this->assertEquals('calendars/user1/inbox/',$prop->getHref()); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-address-set',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set']; $this->assertTrue($prop instanceof DAV\Property\HrefList); $this->assertEquals(array('mailto:user1.sabredav@sabredav.org','/principals/user1/'),$prop->getHrefs()); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}calendar-user-type',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}calendar-user-type']; $this->assertEquals('INDIVIDUAL',$prop); $this->assertArrayHasKey('{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL',$props[0][200]); $prop = $props[0][200]['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL']; $this->assertEquals('calendars/user1/default/',$prop->getHref()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/ScheduleDeliverTest.php000066400000000000000000000343761267035675400250770ustar00rootroot00000000000000 'principals/user1', 'uri' => 'cal', ], [ 'principaluri' => 'principals/user2', 'uri' => 'cal', ], ]; function setUp() { $this->calendarObjectUri = '/calendars/user1/cal/object.ics'; parent::setUp(); } function testNewInvite() { $newObject = <<deliver(null, $newObject); $this->assertItemsInInbox('user2', 1); $expected = <<assertVObjEquals( $expected, $newObject ); } function testNewOnWrongCollection() { $newObject = <<calendarObjectUri = '/calendars/user1/object.ics'; $this->deliver(null, $newObject); $this->assertItemsInInbox('user2', 0); } function testNewInviteSchedulingDisabled() { $newObject = <<deliver(null, $newObject, true); $this->assertItemsInInbox('user2', 0); } function testUpdatedInvite() { $newObject = <<deliver($oldObject, $newObject); $this->assertItemsInInbox('user2', 1); $expected = <<assertVObjEquals( $expected, $newObject ); } function testUpdatedInviteSchedulingDisabled() { $newObject = <<deliver($oldObject, $newObject, true); $this->assertItemsInInbox('user2', 0); } function testUpdatedInviteWrongPath() { $newObject = <<calendarObjectUri = '/calendars/user1/inbox/foo.ics'; $this->deliver($oldObject, $newObject); $this->assertItemsInInbox('user2', 0); } function testDeletedInvite() { $newObject = null; $oldObject = <<deliver($oldObject, $newObject); $this->assertItemsInInbox('user2', 1); } function testDeletedInviteSchedulingDisabled() { $newObject = null; $oldObject = <<deliver($oldObject, $newObject, true); $this->assertItemsInInbox('user2', 0); } /** * A MOVE request will trigger an unbind on a scheduling resource. * * However, we must not treat it as a cancellation, it just got moved to a * different calendar. */ function testUnbindIgnoredOnMove() { $newObject = null; $oldObject = <<server->httpRequest->setMethod('MOVE'); $this->deliver($oldObject, $newObject); $this->assertItemsInInbox('user2', 0); } function testDeletedInviteWrongUrl() { $newObject = null; $oldObject = <<calendarObjectUri = '/calendars/user1/inbox/foo.ics'; $this->deliver($oldObject, $newObject); $this->assertItemsInInbox('user2', 0); } function testReply() { $oldObject = <<putPath('calendars/user2/cal/foo.ics', $oldObject); $this->deliver($oldObject, $newObject); $this->assertItemsInInbox('user2', 1); $this->assertItemsInInbox('user1', 0); $expected = <<assertVObjEquals( $expected, $newObject ); } function testInviteUnknownUser() { $newObject = <<deliver(null, $newObject); $expected = <<assertVObjEquals( $expected, $newObject ); } function testInviteNoInboxUrl() { $newObject = <<server->on('propFind', function($propFind) { $propFind->set('{' . Plugin::NS_CALDAV . '}schedule-inbox-URL', null, 403); }); $this->deliver(null, $newObject); $expected = <<assertVObjEquals( $expected, $newObject ); } function testInviteNoCalendarHomeSet() { $newObject = <<server->on('propFind', function($propFind) { $propFind->set('{' . Plugin::NS_CALDAV . '}calendar-home-set', null, 403); }); $this->deliver(null, $newObject); $expected = <<assertVObjEquals( $expected, $newObject ); } function testInviteNoDefaultCalendar() { $newObject = <<server->on('propFind', function($propFind) { $propFind->set('{' . Plugin::NS_CALDAV . '}schedule-default-calendar-URL', null, 403); }); $this->deliver(null, $newObject); $expected = <<assertVObjEquals( $expected, $newObject ); } function testInviteNoScheduler() { $newObject = <<server->removeAllListeners('schedule'); $this->deliver(null, $newObject); $expected = <<assertVObjEquals( $expected, $newObject ); } function testInviteNoACLPlugin() { $this->setupACL = false; parent::setUp(); $newObject = <<deliver(null, $newObject); $expected = <<assertVObjEquals( $expected, $newObject ); } protected $calendarObjectUri; function deliver($oldObject, &$newObject, $disableScheduling = false) { $this->server->httpRequest->setUrl($this->calendarObjectUri); if ($disableScheduling) { $this->server->httpRequest->setHeader('Schedule-Reply','F'); } if ($oldObject && $newObject) { // update $this->putPath($this->calendarObjectUri, $oldObject); $stream = fopen('php://memory','r+'); fwrite($stream, $newObject); rewind($stream); $modified = false; $this->server->emit('beforeWriteContent', [ $this->calendarObjectUri, $this->server->tree->getNodeForPath($this->calendarObjectUri), &$stream, &$modified ]); if ($modified) { $newObject = $stream; } } elseif ($oldObject && !$newObject) { // delete $this->putPath($this->calendarObjectUri, $oldObject); $this->caldavSchedulePlugin->beforeUnbind( $this->calendarObjectUri ); } else { // create $stream = fopen('php://memory','r+'); fwrite($stream, $newObject); rewind($stream); $modified = false; $this->server->emit('beforeCreateFile', [ $this->calendarObjectUri, &$stream, $this->server->tree->getNodeForPath(dirname($this->calendarObjectUri)), &$modified ]); if ($modified) { $newObject = $stream; } } } /** * Creates or updates a node at the specified path. * * This circumvents sabredav's internal server apis, so all events and * access control is skipped. * * @param string $path * @param string $data * @return void */ function putPath($path, $data) { list($parent, $base) = \Sabre\HTTP\UrlUtil::splitPath($path); $parentNode = $this->server->tree->getNodeForPath($parent); /* if ($parentNode->childExists($base)) { $childNode = $parentNode->getChild($base); $childNode->put($data); } else {*/ $parentNode->createFile($base, $data); //} } function assertItemsInInbox($user, $count) { $inboxNode = $this->server->tree->getNodeForPath('calendars/'.$user.'/inbox'); $this->assertEquals($count, count($inboxNode->getChildren())); } function assertVObjEquals($expected, $actual) { $format = function($data) { $data = trim($data, "\r\n"); $data = str_replace("\r","", $data); // Unfolding lines. $data = str_replace("\n ", "", $data); return $data; }; $this->assertEquals( $format($expected), $format($actual) ); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Schedule/SchedulingObjectTest.php000066400000000000000000000235431267035675400252360ustar00rootroot00000000000000markTestSkipped('SQLite driver is not available'); $this->backend = new Backend\MockScheduling(); $this->data = <<data = <<inbox = new Inbox($this->backend, 'principals/user1'); $this->inbox->createFile('item1.ics', $this->data); } function teardown() { unset($this->inbox); unset($this->backend); } function testSetup() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $this->assertInternalType('string',$children[0]->getName()); $this->assertInternalType('string',$children[0]->get()); $this->assertInternalType('string',$children[0]->getETag()); $this->assertEquals('text/calendar; charset=utf-8', $children[0]->getContentType()); } /** * @expectedException InvalidArgumentException */ function testInvalidArg1() { $obj = new SchedulingObject( new Backend\MockScheduling(array(),array()), array(), array() ); } /** * @expectedException InvalidArgumentException */ function testInvalidArg2() { $obj = new SchedulingObject( new Backend\MockScheduling(array(),array()), array(), array('calendarid' => '1') ); } /** * @depends testSetup * @expectedException \Sabre\DAV\Exception\MethodNotAllowed */ function testPut() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $children[0]->put(''); } /** * @depends testSetup */ function testDelete() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $obj = $children[0]; $obj->delete(); $children2 = $this->inbox->getChildren(); $this->assertEquals(count($children)-1, count($children2)); } /** * @depends testSetup */ function testGetLastModified() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $obj = $children[0]; $lastMod = $obj->getLastModified(); $this->assertTrue(is_int($lastMod) || ctype_digit($lastMod) || is_null($lastMod)); } /** * @depends testSetup */ function testGetSize() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $obj = $children[0]; $size = $obj->getSize(); $this->assertInternalType('int', $size); } function testGetOwner() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $obj = $children[0]; $this->assertEquals('principals/user1', $obj->getOwner()); } function testGetGroup() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $obj = $children[0]; $this->assertNull($obj->getGroup()); } function testGetACL() { $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ), ); $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $obj = $children[0]; $this->assertEquals($expected, $obj->getACL()); } function testDefaultACL() { $backend = new Backend\MockScheduling([], []); $calendarObject = new SchedulingObject($backend, ['calendarid' => 1, 'uri' => 'foo', 'principaluri' => 'principals/user1' ]); $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ), ); $this->assertEquals($expected, $calendarObject->getACL()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetACL() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $obj = $children[0]; $obj->setACL(array()); } function testGet() { $children = $this->inbox->getChildren(); $this->assertTrue($children[0] instanceof SchedulingObject); $obj = $children[0]; $this->assertEquals($this->data, $obj->get()); } function testGetRefetch() { $backend = new Backend\MockScheduling(); $backend->createSchedulingObject('principals/user1', 'foo', 'foo'); $obj = new SchedulingObject($backend, array( 'calendarid' => 1, 'uri' => 'foo', 'principaluri' => 'principals/user1', )); $this->assertEquals('foo', $obj->get()); } function testGetEtag1() { $objectInfo = array( 'calendardata' => 'foo', 'uri' => 'foo', 'etag' => 'bar', 'calendarid' => 1 ); $backend = new Backend\MockScheduling(array(), array()); $obj = new SchedulingObject($backend, $objectInfo); $this->assertEquals('bar', $obj->getETag()); } function testGetEtag2() { $objectInfo = array( 'calendardata' => 'foo', 'uri' => 'foo', 'calendarid' => 1 ); $backend = new Backend\MockScheduling(array(), array()); $obj = new SchedulingObject($backend, $objectInfo); $this->assertEquals('"' . md5('foo') . '"', $obj->getETag()); } function testGetSupportedPrivilegesSet() { $objectInfo = array( 'calendardata' => 'foo', 'uri' => 'foo', 'calendarid' => 1 ); $backend = new Backend\MockScheduling(array(), array()); $obj = new SchedulingObject($backend, $objectInfo); $this->assertNull($obj->getSupportedPrivilegeSet()); } function testGetSize1() { $objectInfo = array( 'calendardata' => 'foo', 'uri' => 'foo', 'calendarid' => 1 ); $backend = new Backend\MockScheduling(array(), array()); $obj = new SchedulingObject($backend, $objectInfo); $this->assertEquals(3, $obj->getSize()); } function testGetSize2() { $objectInfo = array( 'uri' => 'foo', 'calendarid' => 1, 'size' => 4, ); $backend = new Backend\MockScheduling(array(), array()); $obj = new SchedulingObject($backend, $objectInfo); $this->assertEquals(4, $obj->getSize()); } function testGetContentType() { $objectInfo = array( 'uri' => 'foo', 'calendarid' => 1, ); $backend = new Backend\MockScheduling(array(), array()); $obj = new SchedulingObject($backend, $objectInfo); $this->assertEquals('text/calendar; charset=utf-8', $obj->getContentType()); } function testGetContentType2() { $objectInfo = array( 'uri' => 'foo', 'calendarid' => 1, 'component' => 'VEVENT', ); $backend = new Backend\MockScheduling(array(), array()); $obj = new SchedulingObject($backend, $objectInfo); $this->assertEquals('text/calendar; charset=utf-8; component=VEVENT', $obj->getContentType()); } function testGetACL2() { $objectInfo = array( 'uri' => 'foo', 'calendarid' => 1, 'acl' => [], ); $backend = new Backend\MockScheduling(array(), array()); $obj = new SchedulingObject($backend, $objectInfo); $this->assertEquals([], $obj->getACL()); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/ShareableCalendarTest.php000066400000000000000000000027071267035675400236050ustar00rootroot00000000000000 1, ); $this->backend = new Backend\MockSharing( array($props) ); $this->backend->updateShares(1, array( array( 'href' => 'mailto:removeme@example.org', 'commonName' => 'To be removed', 'readOnly' => true, ), ), array()); $this->instance = new ShareableCalendar($this->backend, $props); } function testUpdateShares() { $this->instance->updateShares(array( array( 'href' => 'mailto:test@example.org', 'commonName' => 'Foo Bar', 'summary' => 'Booh', 'readOnly' => false, ), ), array('mailto:removeme@example.org')); $this->assertEquals(array(array( 'href' => 'mailto:test@example.org', 'commonName' => 'Foo Bar', 'summary' => 'Booh', 'readOnly' => false, 'status' => SharingPlugin::STATUS_NORESPONSE, )), $this->instance->getShares()); } function testPublish() { $this->assertNull($this->instance->setPublishStatus(true)); $this->assertNull($this->instance->setPublishStatus(false)); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/SharedCalendarTest.php000066400000000000000000000140601267035675400231200ustar00rootroot00000000000000 1, '{http://calendarserver.org/ns/}shared-url' => 'calendars/owner/original', '{http://sabredav.org/ns}owner-principal' => 'principals/owner', '{http://sabredav.org/ns}read-only' => false, 'principaluri' => 'principals/sharee', ); } $this->backend = new Backend\MockSharing( array($props), array(), array() ); $this->backend->updateShares(1, array( array( 'href' => 'mailto:removeme@example.org', 'commonName' => 'To be removed', 'readOnly' => true, ), ), array()); return new SharedCalendar($this->backend, $props); } function testGetSharedUrl() { $this->assertEquals('calendars/owner/original', $this->getInstance()->getSharedUrl()); } function testGetShares() { $this->assertEquals(array(array( 'href' => 'mailto:removeme@example.org', 'commonName' => 'To be removed', 'readOnly' => true, 'status' => SharingPlugin::STATUS_NORESPONSE, )), $this->getInstance()->getShares()); } function testGetOwner() { $this->assertEquals('principals/owner', $this->getInstance()->getOwner()); } function testGetACL() { $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner/calendar-proxy-read', 'protected' => true, ), array( 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', 'principal' => '{DAV:}authenticated', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/owner', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/owner/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/sharee', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/sharee', 'protected' => true, ), ); $this->assertEquals($expected, $this->getInstance()->getACL()); } function testGetChildACL() { $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner/calendar-proxy-read', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/owner', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/owner/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/sharee', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/sharee', 'protected' => true, ), ); $this->assertEquals($expected, $this->getInstance()->getChildACL()); } function testGetChildACLReadOnly() { $expected = array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/owner/calendar-proxy-read', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => 'principals/sharee', 'protected' => true, ), ); $props = array( 'id' => 1, '{http://calendarserver.org/ns/}shared-url' => 'calendars/owner/original', '{http://sabredav.org/ns}owner-principal' => 'principals/owner', '{http://sabredav.org/ns}read-only' => true, 'principaluri' => 'principals/sharee', ); $this->assertEquals($expected, $this->getInstance($props)->getChildACL()); } /** * @expectedException InvalidArgumentException */ public function testCreateInstanceMissingArg() { $this->getInstance(array( 'id' => 1, '{http://calendarserver.org/ns/}shared-url' => 'calendars/owner/original', '{http://sabredav.org/ns}read-only' => false, 'principaluri' => 'principals/sharee', )); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/SharingPluginTest.php000066400000000000000000000262361267035675400230420ustar00rootroot00000000000000caldavCalendars = array( array( 'principaluri' => 'principals/user1', 'id' => 1, 'uri' => 'cal1', ), array( 'principaluri' => 'principals/user1', 'id' => 2, 'uri' => 'cal2', '{' . Plugin::NS_CALENDARSERVER . '}shared-url' => 'calendars/user1/cal2', '{http://sabredav.org/ns}owner-principal' => 'principals/user2', '{http://sabredav.org/ns}read-only' => 'true', ), array( 'principaluri' => 'principals/user1', 'id' => 3, 'uri' => 'cal3', ), ); parent::setUp(); // Making the logged in user an admin, for full access: $this->aclPlugin->adminPrincipals[] = 'principals/user1'; $this->aclPlugin->adminPrincipals[] = 'principals/user2'; } function testSimple() { $this->assertInstanceOf('Sabre\\CalDAV\\SharingPlugin', $this->server->getPlugin('caldav-sharing')); } function testGetFeatures() { $this->assertEquals(array('calendarserver-sharing'), $this->caldavSharingPlugin->getFeatures()); } function testBeforeGetShareableCalendar() { // Forcing the server to authenticate: $this->authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); $props = $this->server->getProperties('calendars/user1/cal1', array( '{' . Plugin::NS_CALENDARSERVER . '}invite', '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', )); $this->assertInstanceOf('Sabre\\CalDAV\\Property\\Invite', $props['{' . Plugin::NS_CALENDARSERVER . '}invite']); $this->assertInstanceOf('Sabre\\CalDAV\\Property\\AllowedSharingModes', $props['{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes']); } function testBeforeGetSharedCalendar() { $props = $this->server->getProperties('calendars/user1/cal2', array( '{' . Plugin::NS_CALENDARSERVER . '}shared-url', '{' . Plugin::NS_CALENDARSERVER . '}invite', )); $this->assertInstanceOf('Sabre\\CalDAV\\Property\\Invite', $props['{' . Plugin::NS_CALENDARSERVER . '}invite']); $this->assertInstanceOf('Sabre\\DAV\\Property\\IHref', $props['{' . Plugin::NS_CALENDARSERVER . '}shared-url']); } function testUpdateProperties() { $this->caldavBackend->updateShares(1, array( array( 'href' => 'mailto:joe@example.org', ), ), array() ); $result = $this->server->updateProperties('calendars/user1/cal1', array( '{DAV:}resourcetype' => new DAV\Property\ResourceType(array('{DAV:}collection')) )); $this->assertEquals(array( '{DAV:}resourcetype' => 200 ), $result); $this->assertEquals(0, count($this->caldavBackend->getShares(1))); } function testUpdatePropertiesPassThru() { $result = $this->server->updateProperties('calendars/user1/cal3', array( '{DAV:}foo' => 'bar', )); $this->assertEquals(array( '{DAV:}foo' => 403, ), $result); } function testUnknownMethodNoPOST() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PATCH', 'REQUEST_URI' => '/', )); $response = $this->request($request); $this->assertEquals(501, $response->status, $response->body); } function testUnknownMethodNoXML() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/', 'CONTENT_TYPE' => 'text/plain', )); $response = $this->request($request); $this->assertEquals(501, $response->status, $response->body); } function testUnknownMethodNoNode() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/foo', 'CONTENT_TYPE' => 'text/xml', )); $response = $this->request($request); $this->assertEquals(501, $response->status, $response->body); } function testShareRequest() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/cal1', 'CONTENT_TYPE' => 'text/xml', )); $xml = << mailto:joe@example.org Joe Shmoe mailto:nancy@example.org RRR; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(200, $response->status, $response->body); $this->assertEquals(array(array( 'href' => 'mailto:joe@example.org', 'commonName' => 'Joe Shmoe', 'readOnly' => false, 'status' => SharingPlugin::STATUS_NORESPONSE, 'summary' => '', )), $this->caldavBackend->getShares(1)); // Verifying that the calendar is now marked shared. $props = $this->server->getProperties('calendars/user1/cal1', array('{DAV:}resourcetype')); $this->assertTrue( $props['{DAV:}resourcetype']->is('{http://calendarserver.org/ns/}shared-owner') ); } function testShareRequestNoShareableCalendar() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/cal2', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' mailto:joe@example.org Joe Shmoe mailto:nancy@example.org '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(501, $response->status, $response->body); } function testInviteReply() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' /principals/owner '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(200, $response->status, $response->body); } function testInviteBadXML() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(400, $response->status, $response->body); } function testInviteWrongUrl() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/cal1', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' /principals/owner '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(501, $response->status, $response->body); // If the plugin did not handle this request, it must ensure that the // body is still accessible by other plugins. $this->assertEquals($xml, $request->getBody(true)); } function testPublish() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/cal1', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(202, $response->status, $response->body); } function testUnpublish() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/cal1', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(200, $response->status, $response->body); } function testPublishWrongUrl() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/cal2', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(501, $response->status, $response->body); } function testUnpublishWrongUrl() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/cal2', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(501, $response->status, $response->body); } function testUnknownXmlDoc() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'POST', 'REQUEST_URI' => '/calendars/user1/cal2', 'CONTENT_TYPE' => 'text/xml', )); $xml = ' '; $request->setBody($xml); $response = $this->request($request); $this->assertEquals(501, $response->status, $response->body); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Subscriptions/000077500000000000000000000000001267035675400215555ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CalDAV/Subscriptions/CreateSubscriptionTest.php000066400000000000000000000120451267035675400267400ustar00rootroot00000000000000 #1C4587FF Jewish holidays Foo 19 webcal://www.example.org/ P1W XML; $headers = [ 'Content-Type' => 'application/xml', ]; $request = new Request('MKCOL', '/calendars/user1/subscription1', $headers, $body); $response = $this->request($request); $this->assertEquals(201, $response->getStatus()); $subscriptions = $this->caldavBackend->getSubscriptionsForUser('principals/user1'); $this->assertSubscription($subscriptions[0]); } /** * OS X 10.9.2 and up */ function testMKCALENDAR() { $body = << P1W webcal://www.example.org/ #1C4587FF 19 Foo Jewish holidays XML; $headers = [ 'Content-Type' => 'application/xml', ]; $request = new Request('MKCALENDAR', '/calendars/user1/subscription1', $headers, $body); $response = $this->request($request); $this->assertEquals(201, $response->getStatus()); $subscriptions = $this->caldavBackend->getSubscriptionsForUser('principals/user1'); $this->assertSubscription($subscriptions[0]); // Also seeing if it works when calling this as a PROPFIND. $this->assertEquals([ '{http://calendarserver.org/ns/}subscribed-strip-alarms' => '', ], $this->server->getProperties('calendars/user1/subscription1', ['{http://calendarserver.org/ns/}subscribed-strip-alarms']) ); } function assertSubscription($subscription) { $this->assertEquals('', $subscription['{http://calendarserver.org/ns/}subscribed-strip-attachments']); $this->assertEquals('', $subscription['{http://calendarserver.org/ns/}subscribed-strip-todos']); $this->assertEquals('#1C4587FF', $subscription['{http://apple.com/ns/ical/}calendar-color']); $this->assertEquals('Jewish holidays', $subscription['{DAV:}displayname']); $this->assertEquals('Foo', $subscription['{urn:ietf:params:xml:ns:caldav}calendar-description']); $this->assertEquals('19', $subscription['{http://apple.com/ns/ical/}calendar-order']); $this->assertEquals('webcal://www.example.org/', $subscription['{http://calendarserver.org/ns/}source']->getHref()); $this->assertEquals('P1W', $subscription['{http://apple.com/ns/ical/}refreshrate']); $this->assertEquals('subscription1', $subscription['uri']); $this->assertEquals('principals/user1', $subscription['principaluri']); $this->assertEquals('webcal://www.example.org/', $subscription['source']); $this->assertEquals(['principals/user1', 1], $subscription['id']); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Subscriptions/PluginTest.php000066400000000000000000000021571267035675400243710ustar00rootroot00000000000000addPlugin($plugin); $this->assertEquals( '{http://calendarserver.org/ns/}subscribed', $server->resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] ); $this->assertEquals( 'Sabre\\DAV\\Property\\Href', $server->propertyMap['{http://calendarserver.org/ns/}source'] ); $this->assertEquals( ['calendarserver-subscribed'], $plugin->getFeatures() ); } public function testPropFind() { $propName = '{http://calendarserver.org/ns/}subscribed-strip-alarms'; $propFind = new PropFind('foo', [$propName]); $propFind->set($propName,null,200); $plugin = new Plugin(); $plugin->propFind($propFind, new \Sabre\DAV\SimpleCollection('hi')); $this->assertFalse(is_null($propFind->get($propName))); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/Subscriptions/SubscriptionTest.php000066400000000000000000000074411267035675400256200ustar00rootroot00000000000000 new Href('http://example.org/src', false), 'lastmodified' => date('2013-04-06 11:40:00'), // tomorrow is my birthday! '{DAV:}displayname' => 'displayname', ]; $id = $caldavBackend->createSubscription('principals/user1', 'uri', array_merge($info, $override)); $subInfo = $caldavBackend->getSubscriptionsForUser('principals/user1'); $this->assertEquals(1, count($subInfo)); $subscription = new Subscription($caldavBackend, $subInfo[0]); $this->backend = $caldavBackend; return $subscription; } function testValues() { $sub = $this->getSub(); $this->assertEquals('uri', $sub->getName()); $this->assertEquals(date('2013-04-06 11:40:00'), $sub->getLastModified()); $this->assertEquals([], $sub->getChildren()); $this->assertEquals( [ '{DAV:}displayname' => 'displayname', '{http://calendarserver.org/ns/}source' => new Href('http://example.org/src',false), ], $sub->getProperties(['{DAV:}displayname', '{http://calendarserver.org/ns/}source']) ); $this->assertEquals('principals/user1', $sub->getOwner()); $this->assertNull($sub->getGroup()); $acl = [ [ 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}write', 'principal' => 'principals/user1/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', 'principal' => 'principals/user1/calendar-proxy-read', 'protected' => true, ] ]; $this->assertEquals($acl, $sub->getACL()); $this->assertNull($sub->getSupportedPrivilegeSet()); } function testValues2() { $sub = $this->getSub([ 'lastmodified' => null, ]); $this->assertEquals(null, $sub->getLastModified()); } /** * @expectedException \Sabre\DAV\Exception\MethodNotAllowed */ function testSetACL() { $sub = $this->getSub(); $sub->setACL([]); } function testDelete() { $sub = $this->getSub(); $sub->delete(); $this->assertEquals([], $this->backend->getSubscriptionsForUser('principals1/user1')); } function testUpdateProperties() { $sub = $this->getSub(); $propPatch = new PropPatch([ '{DAV:}displayname' => 'foo', ]); $sub->propPatch($propPatch); $this->assertTrue($propPatch->commit()); $this->assertEquals( 'foo', $this->backend->getSubscriptionsForUser('principals/user1')[0]['{DAV:}displayname'] ); } /** * @expectedException \InvalidArgumentException */ function testBadConstruct() { $caldavBackend = new \Sabre\CalDAV\Backend\MockSubscriptionSupport([],[]); new Subscription($caldavBackend, []); } } sabre-dav-2.1.10/tests/Sabre/CalDAV/TestUtil.php000066400000000000000000000144451267035675400212040ustar00rootroot00000000000000setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); // Yup this is definitely not 'fool proof', but good enough for now. $queries = explode(';', file_get_contents(__DIR__ . '/../../../examples/sql/sqlite.calendars.sql')); foreach($queries as $query) { $pdo->exec($query); } // Inserting events through a backend class. $backend = new Backend\PDO($pdo); $calendarId = $backend->createCalendar( 'principals/user1', 'UUID-123467', array( '{DAV:}displayname' => 'user1 calendar', '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description', '{http://apple.com/ns/ical/}calendar-order' => '1', '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', ) ); $backend->createCalendar( 'principals/user1', 'UUID-123468', array( '{DAV:}displayname' => 'user1 calendar2', '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'Calendar description', '{http://apple.com/ns/ical/}calendar-order' => '1', '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', ) ); $backend->createCalendarObject($calendarId, 'UUID-2345', self::getTestCalendarData()); return $pdo; } static function getTestCalendarData($type = 1) { $calendarData = 'BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Apple Inc.//iCal 4.0.1//EN CALSCALE:GREGORIAN BEGIN:VTIMEZONE TZID:Asia/Seoul BEGIN:DAYLIGHT TZOFFSETFROM:+0900 RRULE:FREQ=YEARLY;UNTIL=19880507T150000Z;BYMONTH=5;BYDAY=2SU DTSTART:19870510T000000 TZNAME:GMT+09:00 TZOFFSETTO:+1000 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+1000 DTSTART:19881009T000000 TZNAME:GMT+09:00 TZOFFSETTO:+0900 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20100225T154229Z UID:39A6B5ED-DD51-4AFE-A683-C35EE3749627 TRANSP:TRANSPARENT SUMMARY:Something here DTSTAMP:20100228T130202Z'; switch($type) { case 1 : $calendarData.="\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDTEND;TZID=Asia/Seoul:20100223T070000\n"; break; case 2 : $calendarData.="\nDTSTART:20100223T060000\nDTEND:20100223T070000\n"; break; case 3 : $calendarData.="\nDTSTART;VALUE=DATE:20100223\nDTEND;VALUE=DATE:20100223\n"; break; case 4 : $calendarData.="\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDURATION:PT1H\n"; break; case 5 : $calendarData.="\nDTSTART;TZID=Asia/Seoul:20100223T060000\nDURATION:-P5D\n"; break; case 6 : $calendarData.="\nDTSTART;VALUE=DATE:20100223\n"; break; case 7 : $calendarData.="\nDTSTART;VALUE=DATETIME:20100223T060000\n"; break; // No DTSTART, so intentionally broken case 'X' : $calendarData.="\n"; break; } $calendarData.='ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com SEQUENCE:2 END:VEVENT END:VCALENDAR'; return $calendarData; } static function getTestTODO($type = 'due') { switch($type) { case 'due' : $extra = "DUE:20100104T000000Z"; break; case 'due2' : $extra = "DUE:20060104T000000Z"; break; case 'due_date' : $extra = "DUE;VALUE=DATE:20060104"; break; case 'due_tz' : $extra = "DUE;TZID=Asia/Seoul:20060104T000000Z"; break; case 'due_dtstart' : $extra = "DTSTART:20050223T060000Z\nDUE:20060104T000000Z"; break; case 'due_dtstart2' : $extra = "DTSTART:20090223T060000Z\nDUE:20100104T000000Z"; break; case 'dtstart' : $extra = 'DTSTART:20100223T060000Z'; break; case 'dtstart2' : $extra = 'DTSTART:20060223T060000Z'; break; case 'dtstart_date' : $extra = 'DTSTART;VALUE=DATE:20100223'; break; case 'dtstart_tz' : $extra = 'DTSTART;TZID=Asia/Seoul:20100223T060000Z'; break; case 'dtstart_duration' : $extra = "DTSTART:20061023T060000Z\nDURATION:PT1H"; break; case 'dtstart_duration2' : $extra = "DTSTART:20101023T060000Z\nDURATION:PT1H"; break; case 'completed' : $extra = 'COMPLETED:20060601T000000Z'; break; case 'completed2' : $extra = 'COMPLETED:20090601T000000Z'; break; case 'created' : $extra = 'CREATED:20060601T000000Z'; break; case 'created2' : $extra = 'CREATED:20090601T000000Z'; break; case 'completedcreated' : $extra = "CREATED:20060601T000000Z\nCOMPLETED:20070101T000000Z"; break; case 'completedcreated2' : $extra = "CREATED:20090601T000000Z\nCOMPLETED:20100101T000000Z"; break; case 'notime' : $extra = 'X-FILLER:oh hello'; break; default : throw new Exception('Unknown type: ' . $type); } $todo = 'BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Example Corp.//CalDAV Client//EN BEGIN:VTODO DTSTAMP:20060205T235335Z ' . $extra . ' STATUS:NEEDS-ACTION SUMMARY:Task #1 UID:DDDEEB7915FA61233B861457@example.com BEGIN:VALARM ACTION:AUDIO TRIGGER;RELATED=START:-PT10M END:VALARM END:VTODO END:VCALENDAR'; return $todo; } } sabre-dav-2.1.10/tests/Sabre/CalDAV/ValidateICalTest.php000066400000000000000000000227431267035675400225510ustar00rootroot00000000000000 'calendar1', 'principaluri' => 'principals/admin', 'uri' => 'calendar1', '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Property\SupportedCalendarComponentSet( array('VEVENT','VTODO','VJOURNAL') ), ), array( 'id' => 'calendar2', 'principaluri' => 'principals/admin', 'uri' => 'calendar2', '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Property\SupportedCalendarComponentSet( array('VTODO','VJOURNAL') ), ) ); $this->calBackend = new Backend\Mock($calendars,array()); $principalBackend = new DAVACL\PrincipalBackend\Mock(); $tree = array( new CalendarRoot($principalBackend, $this->calBackend), ); $this->server = new DAV\Server($tree); $this->server->sapi = new HTTP\SapiMock(); $this->server->debugExceptions = true; $plugin = new Plugin(); $this->server->addPlugin($plugin); $response = new HTTP\ResponseMock(); $this->server->httpResponse = $response; } function request(HTTP\Request $request) { $this->server->httpRequest = $request; $this->server->exec(); return $this->server->httpResponse; } function testCreateFile() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $response = $this->request($request); $this->assertEquals(415, $response->status); } function testCreateFileValid() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Length' => ['0'], 'ETag' => ['"' . md5("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n") . '"'], ), $response->getHeaders()); $expected = array( 'uri' => 'blabla.ics', 'calendardata' => "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n", 'calendarid' => 'calendar1', 'lastmodified' => null, ); $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1','blabla.ics')); } function testCreateFileNoComponents() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } function testCreateFileNoUID() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } function testCreateFileVCard() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n"); $response = $this->request($request); $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } function testCreateFile2Components() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nBEGIN:VJOURNAL\r\nUID:foo\r\nEND:VJOURNAL\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } function testCreateFile2UIDS() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nBEGIN:VEVENT\r\nUID:bar\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } function testCreateFileWrongComponent() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VFREEBUSY\r\nUID:foo\r\nEND:VFREEBUSY\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(400, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } function testUpdateFile() { $this->calBackend->createCalendarObject('calendar1','blabla.ics','foo'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $response = $this->request($request); $this->assertEquals(415, $response->status); } function testUpdateFileParsableBody() { $this->calBackend->createCalendarObject('calendar1','blabla.ics','foo'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $body = "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"; $request->setBody($body); $response = $this->request($request); $this->assertEquals(204, $response->status); $expected = array( 'uri' => 'blabla.ics', 'calendardata' => $body, 'calendarid' => 'calendar1', 'lastmodified' => null, ); $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1','blabla.ics')); } function testCreateFileInvalidComponent() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } function testUpdateFileInvalidComponent() { $this->calBackend->createCalendarObject('calendar2','blabla.ics','foo'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } /** * What we are testing here, is if we send in a latin1 character, the * server should automatically transform this into UTF-8. * * More importantly. If any transformation happens, the etag must no longer * be returned by the server. */ function testCreateFileModified() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', )); $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nSUMMARY:Meeting in M\xfcnster\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); $this->assertNull($response->getHeader('ETag')); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/000077500000000000000000000000001267035675400170605ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CardDAV/AbstractPluginTest.php000066400000000000000000000017171267035675400233610ustar00rootroot00000000000000backend = new Backend\Mock(); $principalBackend = new DAVACL\PrincipalBackend\Mock(); $tree = array( new AddressBookRoot($principalBackend, $this->backend), new DAVACL\PrincipalCollection($principalBackend) ); $this->plugin = new Plugin(); $this->plugin->directories = array('directory'); $this->server = new DAV\Server($tree); $this->server->sapi = new HTTP\SapiMock(); $this->server->addPlugin($this->plugin); $this->server->debugExceptions = true; } } sabre-dav-2.1.10/tests/Sabre/CardDAV/AddressBookQueryParserTest.php000066400000000000000000000236651267035675400250500ustar00rootroot00000000000000parse(); return $q; } function testFilterBasic() { $xml = array( '', '', ' ', ' ', ' ', ' ', ' ', ' ', '' ); $q = $this->parse($xml); $this->assertEquals( array('{DAV:}foo'), $q->requestedProperties ); $this->assertEquals( array( array( 'name' => 'NICKNAME', 'test' => 'anyof', 'is-not-defined' => false, 'param-filters' => array(), 'text-matches' => array(), ), ), $q->filters ); $this->assertNull($q->limit); $this->assertEquals('anyof', $q->test); } function testNoFilter() { // This is non-standard, but helps working around a KDE bug $xml = array( '', '', ' ', ' ', ' ', '' ); $q = $this->parse($xml); $this->assertEquals( array('{DAV:}foo'), $q->requestedProperties ); $this->assertEquals( array(), $q->filters ); $this->assertNull($q->limit); $this->assertEquals('anyof', $q->test); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testFilterDoubleFilter() { $xml = array( '', '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '' ); $q = $this->parse($xml); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testFilterCorruptTest() { $xml = array( '', '', ' ', ' ', ' ', ' ', ' ', ' ', '' ); $q = $this->parse($xml); } function testPropFilter() { $xml = array( '', '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' 4', '' ); $q = $this->parse($xml); $this->assertEquals( array( array( 'name' => 'NICKNAME', 'test' => 'anyof', 'is-not-defined' => false, 'param-filters' => array(), 'text-matches' => array(), ), array( 'name' => 'EMAIL', 'test' => 'allof', 'is-not-defined' => false, 'param-filters' => array(), 'text-matches' => array(), ), array( 'name' => 'FN', 'test' => 'anyof', 'is-not-defined' => true, 'param-filters' => array(), 'text-matches' => array(), ), ), $q->filters ); $this->assertEquals(4,$q->limit); $this->assertEquals('allof', $q->test); } function testParamFilter() { $xml = array( '', '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '' ); $q = $this->parse($xml); $this->assertEquals( array( array( 'name' => 'NICKNAME', 'test' => 'anyof', 'is-not-defined' => false, 'param-filters' => array( array( 'name' => 'BLA', 'is-not-defined' => false, 'text-match' => null ), array( 'name' => 'BLA2', 'is-not-defined' => true, 'text-match' => null ), ), 'text-matches' => array(), ), ), $q->filters ); } function testTextMatch() { $xml = array( '', '', ' ', ' ', ' ', ' ', ' ', ' evert', ' evert', ' rene', ' e', ' ', ' foo', ' ', ' ', ' ', '' ); $q = $this->parse($xml); $this->assertEquals( array( array( 'name' => 'NICKNAME', 'test' => 'anyof', 'is-not-defined' => false, 'param-filters' => array( array( 'name' => 'BLA', 'is-not-defined' => false, 'text-match' => array( 'negate-condition' => false, 'collation' => 'i;unicode-casemap', 'match-type' => 'contains', 'value' => 'foo', ), ), ), 'text-matches' => array( array( 'negate-condition' => false, 'collation' => 'i;unicode-casemap', 'match-type' => 'contains', 'value' => 'evert', ), array( 'negate-condition' => false, 'collation' => 'i;octet', 'match-type' => 'contains', 'value' => 'evert', ), array( 'negate-condition' => true, 'collation' => 'i;unicode-casemap', 'match-type' => 'contains', 'value' => 'rene', ), array( 'negate-condition' => false, 'collation' => 'i;unicode-casemap', 'match-type' => 'starts-with', 'value' => 'e', ), ), ), ), $q->filters ); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testBadTextMatch() { $xml = array( '', '', ' ', ' ', ' ', ' ', ' ', ' evert', ' ', ' ', '' ); $q = $this->parse($xml); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/AddressBookQueryTest.php000066400000000000000000000213521267035675400236620ustar00rootroot00000000000000 'REPORT', 'REQUEST_URI' => '/addressbooks/user1/book1', 'HTTP_DEPTH' => '1', )); $request->setBody( ' ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); // using the client for parsing $client = new DAV\Client(array('baseUri'=>'/')); $result = $client->parseMultiStatus($response->body); $this->assertEquals(array( '/addressbooks/user1/book1/card1' => array( 200 => array( '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', ), ), '/addressbooks/user1/book1/card2' => array( 404 => array( '{DAV:}getetag' => null, ), ) ), $result); } function testQueryDepth0() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/addressbooks/user1/book1/card1', 'HTTP_DEPTH' => '0', )); $request->setBody( ' ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); // using the client for parsing $client = new DAV\Client(array('baseUri'=>'/')); $result = $client->parseMultiStatus($response->body); $this->assertEquals(array( '/addressbooks/user1/book1/card1' => array( 200 => array( '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', ), ), ), $result); } function testQueryNoMatch() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/addressbooks/user1/book1', 'HTTP_DEPTH' => '1', )); $request->setBody( ' ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); // using the client for parsing $client = new DAV\Client(array('baseUri'=>'/')); $result = $client->parseMultiStatus($response->body); $this->assertEquals(array(), $result); } function testQueryLimit() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/addressbooks/user1/book1', 'HTTP_DEPTH' => '1', )); $request->setBody( ' 1 ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); // using the client for parsing $client = new DAV\Client(array('baseUri'=>'/')); $result = $client->parseMultiStatus($response->body); $this->assertEquals(array( '/addressbooks/user1/book1/card1' => array( 200 => array( '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD"). '"', ), ), ), $result); } function testJson() { $request = new HTTP\Request( 'REPORT', '/addressbooks/user1/book1/card1', ['Depth' => '0'] ); $request->setBody( ' ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); // using the client for parsing $client = new DAV\Client(array('baseUri'=>'/')); $result = $client->parseMultiStatus($response->body); $vobjVersion = \Sabre\VObject\Version::VERSION; $this->assertEquals(array( '/addressbooks/user1/book1/card1' => array( 200 => array( '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD"). '"', '{urn:ietf:params:xml:ns:carddav}address-data' => '["vcard",[["version",{},"text","4.0"],["prodid",{},"text","-\/\/Sabre\/\/Sabre VObject ' . $vobjVersion . '\/\/EN"],["uid",{},"text","12345"]]]', ), ), ), $result); } function testVCard4() { $request = new HTTP\Request( 'REPORT', '/addressbooks/user1/book1/card1', ['Depth' => '0'] ); $request->setBody( ' ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); // using the client for parsing $client = new DAV\Client(array('baseUri'=>'/')); $result = $client->parseMultiStatus($response->body); $vobjVersion = \Sabre\VObject\Version::VERSION; $this->assertEquals(array( '/addressbooks/user1/book1/card1' => array( 200 => array( '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD"). '"', '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject $vobjVersion//EN\r\nUID:12345\r\nEND:VCARD\r\n", ), ), ), $result); } function testAddressBookDepth0() { $request = new HTTP\Request( 'REPORT', '/addressbooks/user1/book1', ['Depth' => '0'] ); $request->setBody( ' ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(415, $response->status, 'Incorrect status code. Full response body:' . $response->body); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/AddressBookRootTest.php000066400000000000000000000014641267035675400235020ustar00rootroot00000000000000assertEquals('addressbooks', $root->getName()); } function testGetChildForPrincipal() { $pBackend = new DAVACL\PrincipalBackend\Mock(); $cBackend = new Backend\Mock(); $root = new AddressBookRoot($pBackend, $cBackend); $children = $root->getChildren(); $this->assertEquals(3, count($children)); $this->assertInstanceOf('Sabre\\CardDAV\\UserAddressBooks', $children[0]); $this->assertEquals('user1', $children[0]->getName()); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/AddressBookTest.php000066400000000000000000000114111267035675400226270ustar00rootroot00000000000000backend = new Backend\Mock(); $this->ab = new AddressBook( $this->backend, array( 'uri' => 'book1', 'id' => 'foo', '{DAV:}displayname' => 'd-name', 'principaluri' => 'principals/user1', ) ); } function testGetName() { $this->assertEquals('book1', $this->ab->getName()); } function testGetChild() { $card = $this->ab->getChild('card1'); $this->assertInstanceOf('Sabre\\CardDAV\\Card', $card); $this->assertEquals('card1', $card->getName()); } /** * @expectedException Sabre\DAV\Exception\NotFound */ function testGetChildNotFound() { $card = $this->ab->getChild('card3'); } function testGetChildren() { $cards = $this->ab->getChildren(); $this->assertEquals(2, count($cards)); $this->assertEquals('card1', $cards[0]->getName()); $this->assertEquals('card2', $cards[1]->getName()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testCreateDirectory() { $this->ab->createDirectory('name'); } function testCreateFile() { $file = fopen('php://memory','r+'); fwrite($file,'foo'); rewind($file); $this->ab->createFile('card2',$file); $this->assertEquals('foo', $this->backend->cards['foo']['card2']); } function testDelete() { $this->ab->delete(); $this->assertEquals(array(), $this->backend->addressBooks); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetName() { $this->ab->setName('foo'); } function testGetLastModified() { $this->assertNull($this->ab->getLastModified()); } function testUpdateProperties() { $propPatch = new PropPatch([ '{DAV:}displayname' => 'barrr', ]); $this->ab->propPatch($propPatch); $this->assertTrue($propPatch->commit()); $this->assertEquals('barrr', $this->backend->addressBooks[0]['{DAV:}displayname']); } function testGetProperties() { $props = $this->ab->getProperties(array('{DAV:}displayname')); $this->assertEquals(array( '{DAV:}displayname' => 'd-name', ), $props); } function testACLMethods() { $this->assertEquals('principals/user1', $this->ab->getOwner()); $this->assertNull($this->ab->getGroup()); $this->assertEquals(array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), ), $this->ab->getACL()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetACL() { $this->ab->setACL(array()); } function testGetSupportedPrivilegeSet() { $this->assertNull( $this->ab->getSupportedPrivilegeSet() ); } function testGetSyncTokenNoSyncSupport() { $this->assertNull(null, $this->ab->getSyncToken()); } function testGetChangesNoSyncSupport() { $this->assertNull($this->ab->getChanges(1,null)); } function testGetSyncToken() { if (!SABRE_HASSQLITE) { $this->markTestSkipped('Sqlite is required for this test to run'); } $ab = new AddressBook(TestUtil::getBackend(), [ 'id' => 1, '{DAV:}sync-token' => 2]); $this->assertEquals(2, $ab->getSyncToken()); } function testGetSyncToken2() { if (!SABRE_HASSQLITE) { $this->markTestSkipped('Sqlite is required for this test to run'); } $ab = new AddressBook(TestUtil::getBackend(), [ 'id' => 1, '{http://sabredav.org/ns}sync-token' => 2]); $this->assertEquals(2, $ab->getSyncToken()); } function testGetChanges() { if (!SABRE_HASSQLITE) { $this->markTestSkipped('Sqlite is required for this test to run'); } $ab = new AddressBook(TestUtil::getBackend(), [ 'id' => 1, '{DAV:}sync-token' => 2]); $this->assertEquals([ 'syncToken' => 2, 'modified' => [], 'deleted' => [], 'added' => ['UUID-2345'], ], $ab->getChanges(1, 1)); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/Backend/000077500000000000000000000000001267035675400204075ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CardDAV/Backend/AbstractPDOTest.php000066400000000000000000000235341267035675400240750ustar00rootroot00000000000000getPDO(); $this->backend = new PDO($pdo); $pdo->exec('INSERT INTO addressbooks (principaluri, displayname, uri, description, synctoken) VALUES ("principals/user1", "book1", "book1", "addressbook 1", 1)'); $pdo->exec('INSERT INTO cards (addressbookid, carddata, uri, lastmodified, etag, size) VALUES (1, "card1", "card1", 0, "' . md5('card1') . '", 5)'); } public function testGetAddressBooksForUser() { $result = $this->backend->getAddressBooksForUser('principals/user1'); $expected = array( array( 'id' => 1, 'uri' => 'book1', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'book1', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', '{http://calendarserver.org/ns/}getctag' => 1, '{http://sabredav.org/ns}sync-token' => "1" ) ); $this->assertEquals($expected, $result); } public function testUpdateAddressBookInvalidProp() { $propPatch = new PropPatch([ '{DAV:}displayname' => 'updated', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', '{DAV:}foo' => 'bar', ]); $this->backend->updateAddressBook(1, $propPatch); $result = $propPatch->commit(); $this->assertFalse($result); $result = $this->backend->getAddressBooksForUser('principals/user1'); $expected = array( array( 'id' => 1, 'uri' => 'book1', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'book1', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', '{http://calendarserver.org/ns/}getctag' => 1, '{http://sabredav.org/ns}sync-token' => 1 ) ); $this->assertEquals($expected, $result); } public function testUpdateAddressBookNoProps() { $propPatch = new PropPatch([ ]); $this->backend->updateAddressBook(1, $propPatch); $result = $propPatch->commit(); $this->assertTrue($result); $result = $this->backend->getAddressBooksForUser('principals/user1'); $expected = array( array( 'id' => 1, 'uri' => 'book1', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'book1', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', '{http://calendarserver.org/ns/}getctag' => 1, '{http://sabredav.org/ns}sync-token' => 1 ) ); $this->assertEquals($expected, $result); } public function testUpdateAddressBookSuccess() { $propPatch = new PropPatch([ '{DAV:}displayname' => 'updated', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', ]); $this->backend->updateAddressBook(1, $propPatch); $result = $propPatch->commit(); $this->assertTrue($result); $result = $this->backend->getAddressBooksForUser('principals/user1'); $expected = array( array( 'id' => 1, 'uri' => 'book1', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'updated', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'updated', '{http://calendarserver.org/ns/}getctag' => 2, '{http://sabredav.org/ns}sync-token' => 2 ) ); $this->assertEquals($expected, $result); } public function testDeleteAddressBook() { $this->backend->deleteAddressBook(1); $this->assertEquals(array(), $this->backend->getAddressBooksForUser('principals/user1')); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ public function testCreateAddressBookUnsupportedProp() { $this->backend->createAddressBook('principals/user1','book2', array( '{DAV:}foo' => 'bar', )); } public function testCreateAddressBookSuccess() { $this->backend->createAddressBook('principals/user1','book2', array( '{DAV:}displayname' => 'book2', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 2', )); $expected = array( array( 'id' => 1, 'uri' => 'book1', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'book1', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 1', '{http://calendarserver.org/ns/}getctag' => 1, '{http://sabredav.org/ns}sync-token' => 1, ), array( 'id' => 2, 'uri' => 'book2', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'book2', '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' => 'addressbook 2', '{http://calendarserver.org/ns/}getctag' => 1, '{http://sabredav.org/ns}sync-token' => 1, ) ); $result = $this->backend->getAddressBooksForUser('principals/user1'); $this->assertEquals($expected, $result); } public function testGetCards() { $result = $this->backend->getCards(1); $expected = array( array( 'id' => 1, 'uri' => 'card1', 'lastmodified' => 0, 'etag' => '"' . md5('card1') . '"', 'size' => 5 ) ); $this->assertEquals($expected, $result); } public function testGetCard() { $result = $this->backend->getCard(1,'card1'); $expected = array( 'id' => 1, 'uri' => 'card1', 'carddata' => 'card1', 'lastmodified' => 0, 'etag' => '"' . md5('card1') . '"', 'size' => 5 ); $this->assertEquals($expected, $result); } /** * @depends testGetCard */ public function testCreateCard() { $result = $this->backend->createCard(1, 'card2', 'data2'); $this->assertEquals('"' . md5('data2') . '"', $result); $result = $this->backend->getCard(1,'card2'); $this->assertEquals(2, $result['id']); $this->assertEquals('card2', $result['uri']); $this->assertEquals('data2', $result['carddata']); } /** * @depends testCreateCard */ public function testGetMultiple() { $result = $this->backend->createCard(1, 'card2', 'data2'); $result = $this->backend->createCard(1, 'card3', 'data3'); $check = [ [ 'id' => 1, 'uri' => 'card1', 'carddata' => 'card1', 'lastmodified' => 0, ], [ 'id' => 2, 'uri' => 'card2', 'carddata' => 'data2', 'lastmodified' => time(), ], [ 'id' => 3, 'uri' => 'card3', 'carddata' => 'data3', 'lastmodified' => time(), ], ]; $result = $this->backend->getMultipleCards(1, ['card1','card2','card3']); foreach($check as $index=>$node) { foreach($node as $k=>$v) { if ($k!=='lastmodified') { $this->assertEquals($v, $result[$index][$k]); } else { $this->assertTrue(isset($result[$index][$k])); } } } } /** * @depends testGetCard */ public function testUpdateCard() { $result = $this->backend->updateCard(1, 'card1', 'newdata'); $this->assertEquals('"' . md5('newdata') . '"', $result); $result = $this->backend->getCard(1,'card1'); $this->assertEquals(1, $result['id']); $this->assertEquals('newdata', $result['carddata']); } /** * @depends testGetCard */ public function testDeleteCard() { $this->backend->deleteCard(1, 'card1'); $result = $this->backend->getCard(1,'card1'); $this->assertFalse($result); } function testGetChanges() { $backend = $this->backend; $id = $backend->createAddressBook( 'principals/user1', 'bla', [] ); $result = $backend->getChangesForAddressBook($id, null, 1); $this->assertEquals([ 'syncToken' => 1, "added" => [], 'modified' => [], 'deleted' => [], ], $result); $currentToken = $result['syncToken']; $dummyCard = "BEGIN:VCARD\r\nEND:VCARD\r\n"; $backend->createCard($id, "card1.ics", $dummyCard); $backend->createCard($id, "card2.ics", $dummyCard); $backend->createCard($id, "card3.ics", $dummyCard); $backend->updateCard($id, "card1.ics", $dummyCard); $backend->deleteCard($id, "card2.ics"); $result = $backend->getChangesForAddressBook($id, $currentToken, 1); $this->assertEquals([ 'syncToken' => 6, 'modified' => ["card1.ics"], 'deleted' => ["card2.ics"], "added" => ["card3.ics"], ], $result); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/Backend/Mock.php000066400000000000000000000073261267035675400220210ustar00rootroot00000000000000addressBooks = $addressBooks; $this->cards = $cards; if (is_null($this->addressBooks)) { $this->addressBooks = array( array( 'id' => 'foo', 'uri' => 'book1', 'principaluri' => 'principals/user1', '{DAV:}displayname' => 'd-name', ), ); $card2 = fopen('php://memory','r+'); fwrite($card2,"BEGIN:VCARD\nVERSION:3.0\nUID:45678\nEND:VCARD"); rewind($card2); $this->cards = array( 'foo' => array( 'card1' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", 'card2' => $card2, ), ); } } function getAddressBooksForUser($principalUri) { $books = array(); foreach($this->addressBooks as $book) { if ($book['principaluri'] === $principalUri) { $books[] = $book; } } return $books; } /** * Updates properties for an address book. * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documenation for more info and examples. * * @param string $addressBookId * @param \Sabre\DAV\PropPatch $propPatch * @return void */ public function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) { foreach($this->addressBooks as &$book) { if ($book['id'] !== $addressBookId) continue; $propPatch->handleRemaining(function($mutations) use (&$book) { foreach($mutations as $key=>$value) { $book[$key] = $value; } return true; }); } } function createAddressBook($principalUri, $url, array $properties) { $this->addressBooks[] = array_merge($properties, array( 'id' => $url, 'uri' => $url, 'principaluri' => $principalUri, )); } function deleteAddressBook($addressBookId) { foreach($this->addressBooks as $key=>$value) { if ($value['id'] === $addressBookId) unset($this->addressBooks[$key]); } unset($this->cards[$addressBookId]); } function getCards($addressBookId) { $cards = array(); foreach($this->cards[$addressBookId] as $uri=>$data) { $cards[] = array( 'uri' => $uri, 'carddata' => $data, ); } return $cards; } function getCard($addressBookId, $cardUri) { if (!isset($this->cards[$addressBookId][$cardUri])) { return false; } return array( 'uri' => $cardUri, 'carddata' => $this->cards[$addressBookId][$cardUri], ); } function createCard($addressBookId, $cardUri, $cardData) { $this->cards[$addressBookId][$cardUri] = $cardData; } function updateCard($addressBookId, $cardUri, $cardData) { $this->cards[$addressBookId][$cardUri] = $cardData; } function deleteCard($addressBookId, $cardUri) { unset($this->cards[$addressBookId][$cardUri]); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/Backend/PDOMySQLTest.php000066400000000000000000000015251267035675400232730ustar00rootroot00000000000000markTestSkipped('MySQL driver is not available, or not properly configured'); $pdo = \Sabre\TestUtil::getMySQLDB(); if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database'); $pdo->query("DROP TABLE IF EXISTS addressbooks, cards, addressbookchanges"); $queries = explode( ';', file_get_contents(__DIR__ . '/../../../../examples/sql/mysql.addressbook.sql') ); foreach($queries as $query) { $query = trim($query," \r\n\t"); if ($query) $pdo->exec($query); } return $pdo; } } sabre-dav-2.1.10/tests/Sabre/CardDAV/Backend/PDOSqliteTest.php000066400000000000000000000021721267035675400235660ustar00rootroot00000000000000markTestSkipped('SQLite driver is not available'); $pdo = new \PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend'); $pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); $pdo->query("DROP TABLE IF EXISTS addressbooks"); $pdo->query("DROP TABLE IF EXISTS addressbookchanges"); $pdo->query("DROP TABLE IF EXISTS cards"); $queries = explode( ';', file_get_contents(__DIR__ . '/../../../../examples/sql/sqlite.addressbooks.sql') ); foreach($queries as $query) { $query = trim($query," \r\n\t"); if ($query) $pdo->exec($query); } return $pdo; } } sabre-dav-2.1.10/tests/Sabre/CardDAV/CardTest.php000066400000000000000000000116011267035675400213010ustar00rootroot00000000000000backend = new Backend\Mock(); $this->card = new Card( $this->backend, array( 'uri' => 'book1', 'id' => 'foo', 'principaluri' => 'principals/user1', ), array( 'uri' => 'card1', 'addressbookid' => 'foo', 'carddata' => 'card', ) ); } function testGet() { $result = $this->card->get(); $this->assertEquals('card', $result); } function testGet2() { $this->card = new Card( $this->backend, array( 'uri' => 'book1', 'id' => 'foo', 'principaluri' => 'principals/user1', ), array( 'uri' => 'card1', 'addressbookid' => 'foo', ) ); $result = $this->card->get(); $this->assertEquals("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", $result); } /** * @depends testGet */ function testPut() { $file = fopen('php://memory','r+'); fwrite($file, 'newdata'); rewind($file); $this->card->put($file); $result = $this->card->get(); $this->assertEquals('newdata', $result); } function testDelete() { $this->card->delete(); $this->assertEquals(1, count($this->backend->cards['foo'])); } function testGetContentType() { $this->assertEquals('text/vcard; charset=utf-8', $this->card->getContentType()); } function testGetETag() { $this->assertEquals('"' . md5('card') . '"' , $this->card->getETag()); } function testGetETag2() { $card = new Card( $this->backend, array( 'uri' => 'book1', 'id' => 'foo', 'principaluri' => 'principals/user1', ), array( 'uri' => 'card1', 'addressbookid' => 'foo', 'carddata' => 'card', 'etag' => '"blabla"', ) ); $this->assertEquals('"blabla"' , $card->getETag()); } function testGetLastModified() { $this->assertEquals(null, $this->card->getLastModified()); } function testGetSize() { $this->assertEquals(4, $this->card->getSize()); $this->assertEquals(4, $this->card->getSize()); } function testGetSize2() { $card = new Card( $this->backend, array( 'uri' => 'book1', 'id' => 'foo', 'principaluri' => 'principals/user1', ), array( 'uri' => 'card1', 'addressbookid' => 'foo', 'etag' => '"blabla"', 'size' => 4, ) ); $this->assertEquals(4, $card->getSize()); } function testACLMethods() { $this->assertEquals('principals/user1', $this->card->getOwner()); $this->assertNull($this->card->getGroup()); $this->assertEquals(array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), ), $this->card->getACL()); } function testOverrideACL() { $card = new Card( $this->backend, array( 'uri' => 'book1', 'id' => 'foo', 'principaluri' => 'principals/user1', ), array( 'uri' => 'card1', 'addressbookid' => 'foo', 'carddata' => 'card', 'acl' => array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), ), ) ); $this->assertEquals(array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), ), $card->getACL()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetACL() { $this->card->setACL(array()); } function testGetSupportedPrivilegeSet() { $this->assertNull( $this->card->getSupportedPrivilegeSet() ); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/IDirectoryTest.php000066400000000000000000000011371267035675400225100ustar00rootroot00000000000000addPlugin($plugin); $props = $server->getProperties('directory', array('{DAV:}resourcetype')); $this->assertTrue($props['{DAV:}resourcetype']->is('{' . Plugin::NS_CARDDAV . '}directory')); } } class DirectoryMock extends DAV\SimpleCollection implements IDirectory { } sabre-dav-2.1.10/tests/Sabre/CardDAV/MultiGetTest.php000066400000000000000000000057661267035675400222010ustar00rootroot00000000000000 'REPORT', 'REQUEST_URI' => '/addressbooks/user1/book1', )); $request->setBody( ' /addressbooks/user1/book1/card1 ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); // using the client for parsing $client = new DAV\Client(array('baseUri'=>'/')); $result = $client->parseMultiStatus($response->body); $this->assertEquals(array( '/addressbooks/user1/book1/card1' => array( 200 => array( '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:3.0\r\nUID:12345\r\nEND:VCARD\r\n", ) ) ), $result); } function testMultiGetVCard4() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/addressbooks/user1/book1', )); $request->setBody( ' /addressbooks/user1/book1/card1 ' ); $response = new HTTP\ResponseMock(); $this->server->httpRequest = $request; $this->server->httpResponse = $response; $this->server->exec(); $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:' . $response->body); // using the client for parsing $client = new DAV\Client(array('baseUri'=>'/')); $result = $client->parseMultiStatus($response->body); $prodId = "PRODID:-//Sabre//Sabre VObject " . \Sabre\VObject\Version::VERSION . "//EN"; $this->assertEquals(array( '/addressbooks/user1/book1/card1' => array( 200 => array( '{DAV:}getetag' => '"' . md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD") . '"', '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:4.0\r\n$prodId\r\nUID:12345\r\nEND:VCARD\r\n", ) ) ), $result); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/PluginTest.php000066400000000000000000000122461267035675400216740ustar00rootroot00000000000000assertEquals('card', $this->server->xmlNamespaces[Plugin::NS_CARDDAV]); $this->assertEquals('{' . Plugin::NS_CARDDAV . '}addressbook', $this->server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook']); $this->assertTrue(in_array('addressbook', $this->plugin->getFeatures())); } function testSupportedReportSet() { $this->assertEquals(array( '{' . Plugin::NS_CARDDAV . '}addressbook-multiget', '{' . Plugin::NS_CARDDAV . '}addressbook-query', ), $this->plugin->getSupportedReportSet('addressbooks/user1/book1')); } function testSupportedReportSetEmpty() { $this->assertEquals(array( ), $this->plugin->getSupportedReportSet('')); } function testAddressBookHomeSet() { $result = $this->server->getProperties('principals/user1', array('{' . Plugin::NS_CARDDAV . '}addressbook-home-set')); $this->assertEquals(1, count($result)); $this->assertTrue(isset($result['{' . Plugin::NS_CARDDAV . '}addressbook-home-set'])); $this->assertEquals('addressbooks/user1/', $result['{' . Plugin::NS_CARDDAV . '}addressbook-home-set']->getHref()); } function testMeCardTest() { $result = $this->server->getProperties( 'addressbooks/user1', array( '{http://calendarserver.org/ns/}me-card', ) ); $this->assertEquals( array( '{http://calendarserver.org/ns/}me-card' => new DAV\Property\Href('addressbooks/user1/book1/vcard1.vcf') ), $result ); } function testDirectoryGateway() { $result = $this->server->getProperties('principals/user1', array('{' . Plugin::NS_CARDDAV . '}directory-gateway')); $this->assertEquals(1, count($result)); $this->assertTrue(isset($result['{' . Plugin::NS_CARDDAV . '}directory-gateway'])); $this->assertEquals(array('directory'), $result['{' . Plugin::NS_CARDDAV . '}directory-gateway']->getHrefs()); } function testReportPassThrough() { $this->assertNull($this->plugin->report('{DAV:}foo', new \DomDocument())); } function testHTMLActionsPanel() { $output = ''; $r = $this->server->emit('onHTMLActionsPanel', [$this->server->tree->getNodeForPath('addressbooks/user1'), &$output]); $this->assertFalse($r); $this->assertTrue(!!strpos($output,'Display name')); } function testBrowserPostAction() { $r = $this->server->emit('onBrowserPostAction', ['addressbooks/user1', 'mkaddressbook', [ 'name' => 'NEWADDRESSBOOK', '{DAV:}displayname' => 'foo', ]]); $this->assertFalse($r); $addressbooks = $this->backend->getAddressBooksforUser('principals/user1'); $this->assertEquals(2, count($addressbooks)); $newAddressBook = null; foreach($addressbooks as $addressbook) { if ($addressbook['uri'] === 'NEWADDRESSBOOK') { $newAddressBook = $addressbook; break; } } if (!$newAddressBook) $this->fail('Could not find newly created addressbook'); } function testUpdatePropertiesMeCard() { $result = $this->server->updateProperties('addressbooks/user1', array( '{http://calendarserver.org/ns/}me-card' => new DAV\Property\Href('/addressbooks/user1/book1/vcard2',true), )); $this->assertEquals( array( '{http://calendarserver.org/ns/}me-card' => 200, ), $result ); } function testUpdatePropertiesMeCardBadValue() { $result = $this->server->updateProperties('addressbooks/user1', array( '{http://calendarserver.org/ns/}me-card' => new DAV\Property\HrefList(array()), )); $this->assertEquals( array( '{http://calendarserver.org/ns/}me-card' => 400, ), $result ); } function testAddressbookPluginProperties() { $ns = '{' . Plugin::NS_CARDDAV . '}'; $propFind = new DAV\PropFind('addressbooks/user1/book1', [ $ns . 'supported-address-data', $ns . 'supported-collation-set', ]); $node = $this->server->tree->getNodeForPath('addressbooks/user1/book1'); $this->plugin->propFindEarly($propFind, $node); $this->assertInstanceOf( 'Sabre\\CardDAV\\Property\\SupportedAddressData', $propFind->get($ns . 'supported-address-data') ); $this->assertInstanceOf( 'Sabre\\CardDAV\\Property\\SupportedCollationSet', $propFind->get($ns . 'supported-collation-set') ); } function testGetTransform() { $request = new \Sabre\HTTP\Request('GET', '/addressbooks/user1/book1/card1', ['Accept: application/vcard+json']); $response = new \Sabre\HTTP\ResponseMock(); $this->server->invokeMethod($request, $response); $this->assertEquals(200, $response->getStatus()); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/Property/000077500000000000000000000000001267035675400207045ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/CardDAV/Property/SupportedAddressDataTest.php000066400000000000000000000021771267035675400263510ustar00rootroot00000000000000assertInstanceOf('Sabre\CardDAV\Property\SupportedAddressData', $property); } /** * @depends testSimple */ function testSerialize() { $property = new SupportedAddressData(); $doc = new \DOMDocument(); $root = $doc->createElementNS(CardDAV\Plugin::NS_CARDDAV, 'card:root'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' ' . '' . '' . '' . ' ', $xml); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/Property/SupportedCollationSetTest.php000066400000000000000000000021661267035675400265700ustar00rootroot00000000000000assertInstanceOf('Sabre\CardDAV\Property\SupportedCollationSet', $property); } /** * @depends testSimple */ function testSerialize() { $property = new SupportedCollationSet(); $doc = new \DOMDocument(); $root = $doc->createElementNS(CardDAV\Plugin::NS_CARDDAV, 'card:root'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( ' ' . 'i;ascii-casemap' . 'i;octet' . 'i;unicode-casemap' . ' ', $xml); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/SogoStripContentTypeTest.php000066400000000000000000000035371267035675400245670ustar00rootroot00000000000000 1, 'uri' => 'book1', 'principaluri' => 'principals/user1', ), ); protected $carddavCards = array( 1 => array( 'card1.vcf' => "BEGIN:VCARD\nVERSION:3.0\nUID:12345\nEND:VCARD", ), ); function testDontStrip() { $result = $this->server->getProperties('addressbooks/user1/book1/card1.vcf',array('{DAV:}getcontenttype')); $this->assertEquals(array( '{DAV:}getcontenttype' => 'text/vcard; charset=utf-8' ), $result); } function testStrip() { $this->server->httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 Lightning/1.2.1', )); $result = $this->server->getProperties('addressbooks/user1/book1/card1.vcf',array('{DAV:}getcontenttype')); $this->assertEquals(array( '{DAV:}getcontenttype' => 'text/x-vcard' ), $result); } function testDontTouchOtherMimeTypes() { $this->server->httpRequest = new HTTP\Request('GET','/addressbooks/user1/book1/card1.vcf', [ 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 Lightning/1.2.1', ]); $propFind = new PropFind('hello', ['{DAV:}getcontenttype']); $propFind->set('{DAV:}getcontenttype', 'text/plain'); $this->carddavPlugin->propFindLate($propFind, new \Sabre\DAV\SimpleCollection('foo')); $this->assertEquals('text/plain', $propFind->get('{DAV:}getcontenttype')); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/TestUtil.php000066400000000000000000000035621267035675400213540ustar00rootroot00000000000000setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); // Yup this is definitely not 'fool proof', but good enough for now. $queries = explode(';', file_get_contents(__DIR__ . '/../../../examples/sql/sqlite.addressbooks.sql')); foreach($queries as $query) { $pdo->exec($query); } // Inserting events through a backend class. $backend = new Backend\PDO($pdo); $addressbookId = $backend->createAddressBook( 'principals/user1', 'UUID-123467', array( '{DAV:}displayname' => 'user1 addressbook', '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'AddressBook description', ) ); $backend->createAddressBook( 'principals/user1', 'UUID-123468', array( '{DAV:}displayname' => 'user1 addressbook2', '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'AddressBook description', ) ); $backend->createCard($addressbookId, 'UUID-2345', self::getTestCardData()); return $pdo; } static function getTestCardData($type = 1) { $addressbookData = 'BEGIN:VCARD VERSION:3.0 PRODID:-//Acme Inc.//RoadRunner 1.0//EN FN:Wile E. Coyote N:Coyote;Wile;Erroll;; ORG:Acme Inc. UID:39A6B5ED-DD51-4AFE-A683-C35EE3749627 REV:2012-06-20T07:00:39+00:00 END:VCARD'; return $addressbookData; } } sabre-dav-2.1.10/tests/Sabre/CardDAV/UserAddressBooksTest.php000066400000000000000000000067571267035675400236720ustar00rootroot00000000000000backend = new Backend\Mock(); $this->s = new UserAddressBooks( $this->backend, 'principals/user1' ); } function testGetName() { $this->assertEquals('user1', $this->s->getName()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetName() { $this->s->setName('user2'); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testDelete() { $this->s->delete(); } function testGetLastModified() { $this->assertNull($this->s->getLastModified()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testCreateFile() { $this->s->createFile('bla'); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testCreateDirectory() { $this->s->createDirectory('bla'); } function testGetChild() { $child = $this->s->getChild('book1'); $this->assertInstanceOf('Sabre\\CardDAV\\AddressBook', $child); $this->assertEquals('book1', $child->getName()); } /** * @expectedException Sabre\DAV\Exception\NotFound */ function testGetChild404() { $this->s->getChild('book2'); } function testGetChildren() { $children = $this->s->getChildren(); $this->assertEquals(1, count($children)); $this->assertInstanceOf('Sabre\\CardDAV\\AddressBook', $children[0]); $this->assertEquals('book1', $children[0]->getName()); } function testCreateExtendedCollection() { $resourceType = array( '{' . Plugin::NS_CARDDAV . '}addressbook', '{DAV:}collection', ); $this->s->createExtendedCollection('book2', $resourceType, array('{DAV:}displayname' => 'a-book 2')); $this->assertEquals(array( 'id' => 'book2', 'uri' => 'book2', '{DAV:}displayname' => 'a-book 2', 'principaluri' => 'principals/user1', ), $this->backend->addressBooks[1]); } /** * @expectedException Sabre\DAV\Exception\InvalidResourceType */ function testCreateExtendedCollectionInvalid() { $resourceType = array( '{DAV:}collection', ); $this->s->createExtendedCollection('book2', $resourceType, array('{DAV:}displayname' => 'a-book 2')); } function testACLMethods() { $this->assertEquals('principals/user1', $this->s->getOwner()); $this->assertNull($this->s->getGroup()); $this->assertEquals(array( array( 'privilege' => '{DAV:}read', 'principal' => 'principals/user1', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => 'principals/user1', 'protected' => true, ), ), $this->s->getACL()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testSetACL() { $this->s->setACL(array()); } function testGetSupportedPrivilegeSet() { $this->assertNull( $this->s->getSupportedPrivilegeSet() ); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/VCFExportTest.php000066400000000000000000000033111267035675400222470ustar00rootroot00000000000000 'book1', 'uri' => 'book1', 'principaluri' => 'principals/user1', ) ); protected $carddavCards = array( 'book1' => array( "card1" => "BEGIN:VCARD\r\nFN:Person1\r\nEND:VCARD\r\n", "card2" => "BEGIN:VCARD\r\nFN:Person2\r\nEND:VCARD", "card3" => "BEGIN:VCARD\r\nFN:Person3\r\nEND:VCARD\r\n", "card4" => "BEGIN:VCARD\nFN:Person4\nEND:VCARD\n", ) ); function setUp() { parent::setUp(); $this->server->addPlugin( new VCFExportPlugin() ); } function testSimple() { $this->assertInstanceOf('Sabre\\CardDAV\\VCFExportPlugin', $this->server->getPlugin('Sabre\\CardDAV\\VCFExportPlugin')); } function testExport() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_URI' => '/addressbooks/user1/book1?export', 'QUERY_STRING' => 'export', 'REQUEST_METHOD' => 'GET', )); $response = $this->request($request); $this->assertEquals(200, $response->status, $response->body); $expected = "BEGIN:VCARD FN:Person1 END:VCARD BEGIN:VCARD FN:Person2 END:VCARD BEGIN:VCARD FN:Person3 END:VCARD BEGIN:VCARD FN:Person4 END:VCARD "; // We actually expected windows line endings $expected = str_replace("\n","\r\n", $expected); $this->assertEquals($expected, $response->body); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/ValidateFilterTest.php000066400000000000000000000155271267035675400233420ustar00rootroot00000000000000assertTrue($this->plugin->validateFilters($input, $filters, $test), $message); } else { $this->assertFalse($this->plugin->validateFilters($input, $filters, $test), $message); } } function data() { $body1 = << 'title', 'is-not-defined' => false, 'param-filters' => array(), 'text-matches' => array()); // Check if FOO is defined $filter2 = array('name' => 'foo', 'is-not-defined' => false, 'param-filters' => array(), 'text-matches' => array()); // Check if TITLE is not defined $filter3 = array('name' => 'title', 'is-not-defined' => true, 'param-filters' => array(), 'text-matches' => array()); // Check if FOO is not defined $filter4 = array('name' => 'foo', 'is-not-defined' => true, 'param-filters' => array(), 'text-matches' => array()); // Check if TEL[TYPE] is defined $filter5 = array( 'name' => 'tel', 'is-not-defined' => false, 'test' => 'anyof', 'param-filters' => array( array( 'name' => 'type', 'is-not-defined' => false, 'text-match' => null ), ), 'text-matches' => array(), ); // Check if TEL[FOO] is defined $filter6 = $filter5; $filter6['param-filters'][0]['name'] = 'FOO'; // Check if TEL[TYPE] is not defined $filter7 = $filter5; $filter7['param-filters'][0]['is-not-defined'] = true; // Check if TEL[FOO] is not defined $filter8 = $filter5; $filter8['param-filters'][0]['name'] = 'FOO'; $filter8['param-filters'][0]['is-not-defined'] = true; // Combining property filters $filter9 = $filter5; $filter9['param-filters'][] = $filter6['param-filters'][0]; $filter10 = $filter5; $filter10['param-filters'][] = $filter6['param-filters'][0]; $filter10['test'] = 'allof'; // Check if URL contains 'google' $filter11 = array( 'name' => 'url', 'is-not-defined' => false, 'test' => 'anyof', 'param-filters' => array(), 'text-matches' => array( array( 'match-type' => 'contains', 'value' => 'google', 'negate-condition' => false, 'collation' => 'i;octet', ), ), ); // Check if URL contains 'bing' $filter12 = $filter11; $filter12['text-matches'][0]['value'] = 'bing'; // Check if URL does not contain 'google' $filter13 = $filter11; $filter13['text-matches'][0]['negate-condition'] = true; // Check if URL does not contain 'bing' $filter14 = $filter11; $filter14['text-matches'][0]['value'] = 'bing'; $filter14['text-matches'][0]['negate-condition'] = true; // Param filter with text $filter15 = $filter5; $filter15['param-filters'][0]['text-match'] = array( 'match-type' => 'contains', 'value' => 'WORK', 'collation' => 'i;octet', 'negate-condition' => false, ); $filter16 = $filter15; $filter16['param-filters'][0]['text-match']['negate-condition'] = true; // Param filter + text filter $filter17 = $filter5; $filter17['test'] = 'anyof'; $filter17['text-matches'][] = array( 'match-type' => 'contains', 'value' => '444', 'collation' => 'i;octet', 'negate-condition' => false, ); $filter18 = $filter17; $filter18['text-matches'][0]['negate-condition'] = true; $filter18['test'] = 'allof'; return array( // Basic filters array($body1, array($filter1), 'anyof',true), array($body1, array($filter2), 'anyof',false), array($body1, array($filter3), 'anyof',false), array($body1, array($filter4), 'anyof',true), // Combinations array($body1, array($filter1, $filter2), 'anyof',true), array($body1, array($filter1, $filter2), 'allof',false), array($body1, array($filter1, $filter4), 'anyof',true), array($body1, array($filter1, $filter4), 'allof',true), array($body1, array($filter2, $filter3), 'anyof',false), array($body1, array($filter2, $filter3), 'allof',false), // Basic parameters array($body1, array($filter5), 'anyof', true, 'TEL;TYPE is defined, so this should return true'), array($body1, array($filter6), 'anyof', false, 'TEL;FOO is not defined, so this should return false'), array($body1, array($filter7), 'anyof', false, 'TEL;TYPE is defined, so this should return false'), array($body1, array($filter8), 'anyof', true, 'TEL;TYPE is not defined, so this should return true'), // Combined parameters array($body1, array($filter9), 'anyof', true), array($body1, array($filter10), 'anyof', false), // Text-filters array($body1, array($filter11), 'anyof', true), array($body1, array($filter12), 'anyof', false), array($body1, array($filter13), 'anyof', false), array($body1, array($filter14), 'anyof', true), // Param filter with text-match array($body1, array($filter15), 'anyof', true), array($body1, array($filter16), 'anyof', false), // Param filter + text filter array($body1, array($filter17), 'anyof', true), array($body1, array($filter18), 'anyof', false), array($body1, array($filter18), 'anyof', false), ); } } sabre-dav-2.1.10/tests/Sabre/CardDAV/ValidateVCardTest.php000066400000000000000000000116061267035675400231060ustar00rootroot00000000000000 'addressbook1', 'principaluri' => 'principals/admin', 'uri' => 'addressbook1', ) ); $this->cardBackend = new Backend\Mock($addressbooks,array()); $principalBackend = new DAVACL\PrincipalBackend\Mock(); $tree = array( new AddressBookRoot($principalBackend, $this->cardBackend), ); $this->server = new DAV\Server($tree); $this->server->sapi = new HTTP\SapiMock(); $this->server->debugExceptions = true; $plugin = new Plugin(); $this->server->addPlugin($plugin); $response = new HTTP\ResponseMock(); $this->server->httpResponse = $response; } function request(HTTP\Request $request) { $this->server->httpRequest = $request; $this->server->exec(); return $this->server->httpResponse; } function testCreateFile() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', )); $response = $this->request($request); $this->assertEquals(415, $response->status); } function testCreateFileValid() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', )); $request->setBody("BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n"); $response = $this->request($request); $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); $expected = array( 'uri' => 'blabla.vcf', 'carddata' => "BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n", ); $this->assertEquals($expected, $this->cardBackend->getCard('addressbook1','blabla.vcf')); } function testCreateFileNoUID() { $request = new HTTP\Request( 'PUT', '/addressbooks/admin/addressbook1/blabla.vcf' ); $request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n"); $response = $this->request($request); $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); $foo = $this->cardBackend->getCard('addressbook1','blabla.vcf'); $this->assertTrue(strpos($foo['carddata'],'UID')!==false); } function testCreateFileJson() { $request = new HTTP\Request( 'PUT', '/addressbooks/admin/addressbook1/blabla.vcf' ); $request->setBody('[ "vcard" , [ [ "UID" , {}, "text", "foo" ] ] ]'); $response = $this->request($request); $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); $foo = $this->cardBackend->getCard('addressbook1','blabla.vcf'); $this->assertEquals("BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n", $foo['carddata']); } function testCreateFileVCalendar() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', )); $request->setBody("BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n"); $response = $this->request($request); $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); } function testUpdateFile() { $this->cardBackend->createCard('addressbook1','blabla.vcf','foo'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', )); $response = $this->request($request); $this->assertEquals(415, $response->status); } function testUpdateFileParsableBody() { $this->cardBackend->createCard('addressbook1','blabla.vcf','foo'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/addressbooks/admin/addressbook1/blabla.vcf', )); $body = "BEGIN:VCARD\r\nUID:foo\r\nEND:VCARD\r\n"; $request->setBody($body); $response = $this->request($request); $this->assertEquals(204, $response->status); $expected = array( 'uri' => 'blabla.vcf', 'carddata' => $body, ); $this->assertEquals($expected, $this->cardBackend->getCard('addressbook1','blabla.vcf')); } } ?> sabre-dav-2.1.10/tests/Sabre/DAV/000077500000000000000000000000001267035675400162665ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/DAV/AbstractServer.php000066400000000000000000000026651267035675400217420ustar00rootroot00000000000000response = new HTTP\ResponseMock(); $this->server = new Server($this->getRootNode()); $this->server->sapi = new HTTP\SapiMock(); $this->server->httpResponse = $this->response; $this->server->debugExceptions = true; $this->deleteTree(SABRE_TEMPDIR,false); file_put_contents(SABRE_TEMPDIR . '/test.txt', 'Test contents'); mkdir(SABRE_TEMPDIR . '/dir'); file_put_contents(SABRE_TEMPDIR . '/dir/child.txt', 'Child contents'); } function tearDown() { $this->deleteTree(SABRE_TEMPDIR,false); } protected function getRootNode() { return new FS\Directory(SABRE_TEMPDIR); } private function deleteTree($path,$deleteRoot = true) { foreach(scandir($path) as $node) { if ($node=='.' || $node=='.svn' || $node=='..') continue; $myPath = $path.'/'. $node; if (is_file($myPath)) { unlink($myPath); } else { $this->deleteTree($myPath); } } if ($deleteRoot) rmdir($path); } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/000077500000000000000000000000001267035675400171675ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/000077500000000000000000000000001267035675400205165ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/AbstractBasicTest.php000066400000000000000000000043121267035675400245740ustar00rootroot00000000000000httpResponse = $response; $backend = new AbstractBasicMock(); $backend->authenticate($server,'myRealm'); } /** * @expectedException Sabre\DAV\Exception\NotAuthenticated */ public function testAuthenticateUnknownUser() { $response = new HTTP\ResponseMock(); $tree = new DAV\Tree(new DAV\SimpleCollection('bla')); $server = new DAV\Server($tree); $server->httpResponse = $response; $request = HTTP\Sapi::createFromServerArray(array( 'PHP_AUTH_USER' => 'username', 'PHP_AUTH_PW' => 'wrongpassword', )); $server->httpRequest = $request; $backend = new AbstractBasicMock(); $backend->authenticate($server,'myRealm'); } public function testAuthenticate() { $response = new HTTP\ResponseMock(); $tree = new DAV\Tree(new DAV\SimpleCollection('bla')); $server = new DAV\Server($tree); $server->httpResponse = $response; $request = HTTP\Sapi::createFromServerArray(array( 'PHP_AUTH_USER' => 'username', 'PHP_AUTH_PW' => 'password', )); $server->httpRequest = $request; $backend = new AbstractBasicMock(); $this->assertTrue($backend->authenticate($server,'myRealm')); $result = $backend->getCurrentUser(); $this->assertEquals('username', $result); } } class AbstractBasicMock extends AbstractBasic { /** * Validates a username and password * * This method should return true or false depending on if login * succeeded. * * @param string $username * @param string $password * @return bool */ function validateUserPass($username, $password) { return ($username == 'username' && $password == 'password'); } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/AbstractDigestTest.php000066400000000000000000000103631267035675400247750ustar00rootroot00000000000000httpResponse = $response; $backend = new AbstractDigestMock(); $backend->authenticate($server,'myRealm'); } /** * @expectedException Sabre\DAV\Exception */ public function testAuthenticateBadGetUserInfoResponse() { $response = new HTTP\ResponseMock(); $server = new DAV\Server(); $server->httpResponse = $response; $header = 'username=null, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; $request = HTTP\Sapi::createFromServerArray(array( 'PHP_AUTH_DIGEST' => $header, )); $server->httpRequest = $request; $backend = new AbstractDigestMock(); $backend->authenticate($server,'myRealm'); } /** * @expectedException Sabre\DAV\Exception */ public function testAuthenticateBadGetUserInfoResponse2() { $response = new HTTP\ResponseMock(); $server = new DAV\Server(); $server->httpResponse = $response; $header = 'username=array, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; $request = HTTP\Sapi::createFromServerArray(array( 'PHP_AUTH_DIGEST' => $header, )); $server->httpRequest = $request; $backend = new AbstractDigestMock(); $backend->authenticate($server,'myRealm'); } /** * @expectedException Sabre\DAV\Exception\NotAuthenticated */ public function testAuthenticateUnknownUser() { $response = new HTTP\ResponseMock(); $server = new DAV\Server(); $server->httpResponse = $response; $header = 'username=false, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; $request = HTTP\Sapi::createFromServerArray(array( 'PHP_AUTH_DIGEST' => $header, )); $server->httpRequest = $request; $backend = new AbstractDigestMock(); $backend->authenticate($server,'myRealm'); } /** * @expectedException Sabre\DAV\Exception\NotAuthenticated */ public function testAuthenticateBadPassword() { $response = new HTTP\ResponseMock(); $server = new DAV\Server(); $server->httpResponse = $response; $header = 'username=user, realm=myRealm, nonce=12345, uri=/, response=HASH, opaque=1, qop=auth, nc=1, cnonce=1'; $request = HTTP\Sapi::createFromServerArray(array( 'PHP_AUTH_DIGEST' => $header, 'REQUEST_METHOD' => 'PUT', )); $server->httpRequest = $request; $backend = new AbstractDigestMock(); $backend->authenticate($server,'myRealm'); } public function testAuthenticate() { $response = new HTTP\ResponseMock(); $server = new DAV\Server(); $server->httpResponse = $response; $digestHash = md5('HELLO:12345:1:1:auth:' . md5('GET:/')); $header = 'username=user, realm=myRealm, nonce=12345, uri=/, response='.$digestHash.', opaque=1, qop=auth, nc=1, cnonce=1'; $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'GET', 'PHP_AUTH_DIGEST' => $header, 'REQUEST_URI' => '/', )); $server->httpRequest = $request; $backend = new AbstractDigestMock(); $this->assertTrue($backend->authenticate($server,'myRealm')); $result = $backend->getCurrentUser(); $this->assertEquals('user', $result); $this->assertEquals('HELLO', $backend->getDigestHash('myRealm', $result)); } } class AbstractDigestMock extends AbstractDigest { function getDigestHash($realm, $userName) { switch($userName) { case 'null' : return null; case 'false' : return false; case 'array' : return array(); case 'user' : return 'HELLO'; } } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/AbstractPDOTest.php000066400000000000000000000012161267035675400241750ustar00rootroot00000000000000getPDO(); $backend = new PDO($pdo); $this->assertTrue($backend instanceof PDO); } /** * @depends testConstruct */ function testUserInfo() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $this->assertNull($backend->getDigestHash('realm','blabla')); $expected = 'hash'; $this->assertEquals($expected, $backend->getDigestHash('realm','user')); } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/ApacheTest.php000066400000000000000000000025421267035675400232530ustar00rootroot00000000000000assertInstanceOf('Sabre\DAV\Auth\Backend\Apache', $backend); } /** * @expectedException Sabre\DAV\Exception */ function testNoHeader() { $server = new DAV\Server(); $backend = new Apache(); $backend->authenticate($server,'Realm'); } function testRemoteUser() { $backend = new Apache(); $server = new DAV\Server(); $request = HTTP\Sapi::createFromServerArray([ 'REMOTE_USER' => 'username', ]); $server->httpRequest = $request; $this->assertTrue($backend->authenticate($server, 'Realm')); $userInfo = 'username'; $this->assertEquals($userInfo, $backend->getCurrentUser()); } function testRedirectRemoteUser() { $backend = new Apache(); $server = new DAV\Server(); $request = HTTP\Sapi::createFromServerArray([ 'REDIRECT_REMOTE_USER' => 'username', ]); $server->httpRequest = $request; $this->assertTrue($backend->authenticate($server, 'Realm')); $userInfo = 'username'; $this->assertEquals($userInfo, $backend->getCurrentUser()); } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/BasicCallBackTest.php000066400000000000000000000012761267035675400244730ustar00rootroot00000000000000httpRequest = Sapi::createFromServerArray([ 'HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('foo:bar'), ]); $this->assertTrue($backend->authenticate($server, 'Realm')); $this->assertEquals(['foo','bar'], $args); } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/FileTest.php000066400000000000000000000017351267035675400227540ustar00rootroot00000000000000assertTrue($file instanceof File); } /** * @expectedException Sabre\DAV\Exception */ function testLoadFileBroken() { file_put_contents(SABRE_TEMPDIR . '/backend','user:realm:hash'); $file = new File(); $file->loadFile(SABRE_TEMPDIR .'/backend'); } function testLoadFile() { file_put_contents(SABRE_TEMPDIR . '/backend','user:realm:' . md5('user:realm:password')); $file = new File(); $file->loadFile(SABRE_TEMPDIR . '/backend'); $this->assertFalse($file->getDigestHash('realm','blabla')); $this->assertEquals(md5('user:realm:password'), $file->getDigesthash('realm','user')); } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/Mock.php000066400000000000000000000012311267035675400221150ustar00rootroot00000000000000currentUser = $this->defaultUser; } function setCurrentUser($user) { $this->currentUser = $user; } function getCurrentUser() { return $this->currentUser; } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/PDOMySQLTest.php000066400000000000000000000014731267035675400234040ustar00rootroot00000000000000markTestSkipped('MySQL driver is not available, or not properly configured'); $pdo = \Sabre\TestUtil::getMySQLDB(); if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database'); $pdo->query("DROP TABLE IF EXISTS users"); $pdo->query(" create table users ( id integer unsigned not null primary key auto_increment, username varchar(50), digesta1 varchar(32), email varchar(80), displayname varchar(80), unique(username) );"); $pdo->query("INSERT INTO users (username,digesta1,email,displayname) VALUES ('user','hash','user@example.org','User')"); return $pdo; } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/Backend/PDOSqliteTest.php000066400000000000000000000015421267035675400236750ustar00rootroot00000000000000markTestSkipped('SQLite driver is not available'); $pdo = new \PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend'); $pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); $pdo->query('CREATE TABLE users (username TEXT, digesta1 TEXT, email VARCHAR(80), displayname VARCHAR(80))'); $pdo->query('INSERT INTO users VALUES ("user","hash","user@example.org","User")'); return $pdo; } } sabre-dav-2.1.10/tests/Sabre/DAV/Auth/PluginTest.php000066400000000000000000000052671267035675400220100ustar00rootroot00000000000000assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('auth')); } /** * @depends testInit */ function testAuthenticate() { $fakeServer = new DAV\Server( new DAV\SimpleCollection('bla')); $plugin = new Plugin(new Backend\Mock(),'realm'); $fakeServer->addPlugin($plugin); $this->assertTrue( $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]) ); } /** * @depends testInit * @expectedException Sabre\DAV\Exception\NotAuthenticated */ function testAuthenticateFail() { $fakeServer = new DAV\Server( new DAV\SimpleCollection('bla')); $plugin = new Plugin(new Backend\Mock(),'failme'); $fakeServer->addPlugin($plugin); $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]); } function testReportPassThrough() { $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); $plugin = new Plugin(new Backend\Mock(),'realm'); $fakeServer->addPlugin($plugin); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_CONTENT_TYPE' => 'application/xml', 'REQUEST_URI' => '/', )); $request->setBody(''); $fakeServer->httpRequest = $request; $fakeServer->sapi = new HTTP\SapiMock(); $fakeServer->httpResponse = new HTTP\ResponseMock(); $fakeServer->exec(); $this->assertEquals(415, $fakeServer->httpResponse->status); } /** * @depends testInit */ function testGetCurrentUserPrincipal() { $fakeServer = new DAV\Server( new DAV\SimpleCollection('bla')); $plugin = new Plugin(new Backend\Mock(),'realm'); $fakeServer->addPlugin($plugin); $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]); $this->assertEquals('admin', $plugin->getCurrentUser()); } /** * @depends testInit */ function testPlugin() { $myRealmName = 'some_realm'; $plugin = new Plugin(new Backend\Mock(),$myRealmName); $this->assertEquals($myRealmName, $plugin->getRealm()); } } sabre-dav-2.1.10/tests/Sabre/DAV/BasicNodeTest.php000066400000000000000000000111531267035675400214670ustar00rootroot00000000000000put('hi'); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ public function testGet() { $file = new FileMock(); $file->get(); } public function testGetSize() { $file = new FileMock(); $this->assertEquals(0,$file->getSize()); } public function testGetETag() { $file = new FileMock(); $this->assertNull($file->getETag()); } public function testGetContentType() { $file = new FileMock(); $this->assertNull($file->getContentType()); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ public function testDelete() { $file = new FileMock(); $file->delete(); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ public function testSetName() { $file = new FileMock(); $file->setName('hi'); } public function testGetLastModified() { $file = new FileMock(); // checking if lastmod is within the range of a few seconds $lastMod = $file->getLastModified(); $compareTime = ($lastMod + 1)-time(); $this->assertTrue($compareTime < 3); } public function testGetChild() { $dir = new DirectoryMock(); $file = $dir->getChild('mockfile'); $this->assertTrue($file instanceof FileMock); } public function testChildExists() { $dir = new DirectoryMock(); $this->assertTrue($dir->childExists('mockfile')); } public function testChildExistsFalse() { $dir = new DirectoryMock(); $this->assertFalse($dir->childExists('mockfile2')); } /** * @expectedException Sabre\DAV\Exception\NotFound */ public function testGetChild404() { $dir = new DirectoryMock(); $file = $dir->getChild('blabla'); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ public function testCreateFile() { $dir = new DirectoryMock(); $dir->createFile('hello','data'); } /** * @expectedException Sabre\DAV\Exception\Forbidden */ public function testCreateDirectory() { $dir = new DirectoryMock(); $dir->createDirectory('hello'); } public function testSimpleDirectoryConstruct() { $dir = new SimpleCollection('simpledir',array()); $this->assertInstanceOf('Sabre\DAV\SimpleCollection', $dir); } /** * @depends testSimpleDirectoryConstruct */ public function testSimpleDirectoryConstructChild() { $file = new FileMock(); $dir = new SimpleCollection('simpledir',array($file)); $file2 = $dir->getChild('mockfile'); $this->assertEquals($file,$file2); } /** * @expectedException Sabre\DAV\Exception * @depends testSimpleDirectoryConstruct */ public function testSimpleDirectoryBadParam() { $dir = new SimpleCollection('simpledir',array('string shouldn\'t be here')); } /** * @depends testSimpleDirectoryConstruct */ public function testSimpleDirectoryAddChild() { $file = new FileMock(); $dir = new SimpleCollection('simpledir'); $dir->addChild($file); $file2 = $dir->getChild('mockfile'); $this->assertEquals($file,$file2); } /** * @depends testSimpleDirectoryConstruct * @depends testSimpleDirectoryAddChild */ public function testSimpleDirectoryGetChildren() { $file = new FileMock(); $dir = new SimpleCollection('simpledir'); $dir->addChild($file); $this->assertEquals(array($file),$dir->getChildren()); } /* * @depends testSimpleDirectoryConstruct */ public function testSimpleDirectoryGetName() { $dir = new SimpleCollection('simpledir'); $this->assertEquals('simpledir',$dir->getName()); } /** * @depends testSimpleDirectoryConstruct * @expectedException Sabre\DAV\Exception\NotFound */ public function testSimpleDirectoryGetChild404() { $dir = new SimpleCollection('simpledir'); $dir->getChild('blabla'); } } class DirectoryMock extends Collection { function getName() { return 'mockdir'; } function getChildren() { return array(new FileMock()); } } class FileMock extends File { function getName() { return 'mockfile'; } } sabre-dav-2.1.10/tests/Sabre/DAV/Browser/000077500000000000000000000000001267035675400177115ustar00rootroot00000000000000sabre-dav-2.1.10/tests/Sabre/DAV/Browser/GuessContentTypeTest.php000066400000000000000000000037031267035675400245500ustar00rootroot00000000000000server->getPropertiesForPath('/somefile.jpg',$properties); $this->assertArrayHasKey(0,$result); $this->assertArrayHasKey(404,$result[0]); $this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][404]); } /** * @depends testGetProperties */ function testGetPropertiesPluginEnabled() { $this->server->addPlugin(new GuessContentType()); $properties = array( '{DAV:}getcontenttype', ); $result = $this->server->getPropertiesForPath('/somefile.jpg',$properties); $this->assertArrayHasKey(0,$result); $this->assertArrayHasKey(200,$result[0], 'We received: ' . print_r($result,true)); $this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][200]); $this->assertEquals('image/jpeg',$result[0][200]['{DAV:}getcontenttype']); } /** * @depends testGetPropertiesPluginEnabled */ function testGetPropertiesUnknown() { $this->server->addPlugin(new GuessContentType()); $properties = array( '{DAV:}getcontenttype', ); $result = $this->server->getPropertiesForPath('/somefile.hoi',$properties); $this->assertArrayHasKey(0,$result); $this->assertArrayHasKey(404,$result[0]); $this->assertArrayHasKey('{DAV:}getcontenttype',$result[0][404]); } } sabre-dav-2.1.10/tests/Sabre/DAV/Browser/MapGetToPropFindTest.php000066400000000000000000000020751267035675400244100ustar00rootroot00000000000000server->addPlugin(new MapGetToPropFind()); } function testCollectionGet() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody(''); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(207, $this->response->status,'Incorrect status response received. Full response body: ' . $this->response->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'DAV' => ['1, 3, extended-mkcol'], 'Vary' => ['Brief,Prefer'], ), $this->response->getHeaders() ); } } sabre-dav-2.1.10/tests/Sabre/DAV/Browser/PluginTest.php000066400000000000000000000116221267035675400225220ustar00rootroot00000000000000server->addPlugin($this->plugin = new Plugin()); $this->server->tree->getNodeForPath('')->createDirectory('dir2'); } function testCollectionGet() { $request = new HTTP\Request('GET', '/dir'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(200, $this->response->getStatus(), "Incorrect status received. Full response body: " . $this->response->getBodyAsString()); $this->assertEquals( [ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['text/html; charset=utf-8'], 'Content-Security-Policy' => ["img-src 'self'; style-src 'self';"] ], $this->response->getHeaders() ); $this->assertTrue(strpos($this->response->body, 'dir/') !== false); $this->assertTrue(strpos($this->response->body, '<a href="/dir/child.txt">')!==false); } function testCollectionGetRoot() { $request = new HTTP\Request('GET', '/'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(200, $this->response->status, "Incorrect status received. Full response body: " . $this->response->getBodyAsString()); $this->assertEquals( [ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['text/html; charset=utf-8'], 'Content-Security-Policy' => ["img-src 'self'; style-src 'self';"] ], $this->response->getHeaders() ); $this->assertTrue(strpos($this->response->body, '<title>/') !== false); $this->assertTrue(strpos($this->response->body, '<a href="/dir/">')!==false); $this->assertTrue(strpos($this->response->body, '<span class="btn disabled">')!==false); } function testGETPassthru() { $request = new HTTP\Request('GET', '/random'); $response = new HTTP\Response(); $this->assertNull( $this->plugin->httpGet($request, $response) ); } function testPostOtherContentType() { $request = new HTTP\Request('POST', '/', ['Content-Type' => 'text/xml']); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(501, $this->response->status); } function testPostNoSabreAction() { $request = new HTTP\Request('POST', '/', ['Content-Type' => 'application/x-www-form-urlencoded']); $request->setPostData([]); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(501, $this->response->status); } function testPostMkCol() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'POST', 'CONTENT_TYPE' => 'application/x-www-form-urlencoded', ); $postVars = array( 'sabreAction' => 'mkcol', 'name' => 'new_collection', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setPostData($postVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(302, $this->response->status); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Location' => ['/'], ), $this->response->getHeaders()); $this->assertTrue(is_dir(SABRE_TEMPDIR . '/new_collection')); } function testGetAsset() { $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=favicon.ico'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(200, $this->response->getStatus(), 'Error: ' . $this->response->body); $this->assertEquals([ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['image/vnd.microsoft.icon'], 'Content-Length' => ['4286'], 'Cache-Control' => ['public, max-age=1209600'], ], $this->response->getHeaders()); } function testGetAsset404() { $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=flavicon.ico'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(404, $this->response->getStatus(), 'Error: ' . $this->response->body); } function testGetAssetEscapeBasePath() { $request = new HTTP\Request('GET', '/?sabreAction=asset&assetName=./../assets/favicon.ico'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(404, $this->response->getStatus(), 'Error: ' . $this->response->body); } } ��������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Browser/PropFindAllTest.php����������������������������������������0000664�0000000�0000000�00000003233�12670356754�0023435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Browser; class PropFindAllTest extends \PHPUnit_Framework_TestCase { function testHandleSimple() { $pf = new PropFindAll('foo'); $pf->handle('{DAV:}displayname', 'foo'); $this->assertEquals(200, $pf->getStatus('{DAV:}displayname')); $this->assertEquals('foo', $pf->get('{DAV:}displayname')); } function testHandleCallBack() { $pf = new PropFindAll('foo'); $pf->handle('{DAV:}displayname', function() { return 'foo'; }); $this->assertEquals(200, $pf->getStatus('{DAV:}displayname')); $this->assertEquals('foo', $pf->get('{DAV:}displayname')); } function testSet() { $pf = new PropFindAll('foo'); $pf->set('{DAV:}displayname', 'foo'); $this->assertEquals(200, $pf->getStatus('{DAV:}displayname')); $this->assertEquals('foo', $pf->get('{DAV:}displayname')); } function testSetNull() { $pf = new PropFindAll('foo'); $pf->set('{DAV:}displayname', null); $this->assertEquals(404, $pf->getStatus('{DAV:}displayname')); $this->assertEquals(null, $pf->get('{DAV:}displayname')); } function testGet404Properties() { $pf = new PropFindAll('foo'); $pf->set('{DAV:}displayname', null); $this->assertEquals( ['{DAV:}displayname'], $pf->get404Properties() ); } function testGet404PropertiesNothing() { $pf = new PropFindAll('foo'); $pf->set('{DAV:}displayname', 'foo'); $this->assertEquals( ['{http://sabredav.org/ns}idk'], $pf->get404Properties() ); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ClientMock.php�����������������������������������������������������0000664�0000000�0000000�00000001021�12670356754�0021021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP\RequestInterface; class ClientMock extends Client { public $request; public $response; public $url; public $curlSettings; /** * Just making this method public * * @param string $url * @return string */ public function getAbsoluteUrl($url) { return parent::getAbsoluteUrl($url); } public function doRequest(RequestInterface $request) { $this->request = $request; return $this->response; } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ClientTest.php�����������������������������������������������������0000664�0000000�0000000�00000014603�12670356754�0021061�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP\Request; use Sabre\HTTP\Response; require_once 'Sabre/DAV/ClientMock.php'; class ClientTest extends \PHPUnit_Framework_TestCase { function setUp() { if (!function_exists('curl_init')) { $this->markTestSkipped('CURL must be installed to test the client'); } } function testConstruct() { $client = new ClientMock(array( 'baseUri' => '/', )); $this->assertInstanceOf('Sabre\DAV\ClientMock', $client); } /** * @expectedException InvalidArgumentException */ function testConstructNoBaseUri() { $client = new ClientMock(array()); } function testAuth() { $client = new ClientMock([ 'baseUri' => '/', 'userName' => 'foo', 'password' => 'bar', ]); $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]); $this->assertEquals(CURLAUTH_BASIC | CURLAUTH_DIGEST, $client->curlSettings[CURLOPT_HTTPAUTH]); } function testBasicAuth() { $client = new ClientMock([ 'baseUri' => '/', 'userName' => 'foo', 'password' => 'bar', 'authType' => Client::AUTH_BASIC ]); $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]); $this->assertEquals(CURLAUTH_BASIC, $client->curlSettings[CURLOPT_HTTPAUTH]); } function testDigestAuth() { $client = new ClientMock([ 'baseUri' => '/', 'userName' => 'foo', 'password' => 'bar', 'authType' => Client::AUTH_DIGEST ]); $this->assertEquals("foo:bar", $client->curlSettings[CURLOPT_USERPWD]); $this->assertEquals(CURLAUTH_DIGEST, $client->curlSettings[CURLOPT_HTTPAUTH]); } function testProxy() { $client = new ClientMock([ 'baseUri' => '/', 'proxy' => 'localhost:8888', ]); $this->assertEquals("localhost:8888", $client->curlSettings[CURLOPT_PROXY]); } function testEncoding() { $client = new ClientMock([ 'baseUri' => '/', 'encoding' => Client::ENCODING_IDENTITY | Client::ENCODING_GZIP | Client::ENCODING_DEFLATE, ]); $this->assertEquals("identity,deflate,gzip", $client->curlSettings[CURLOPT_ENCODING]); } function testCAInfo() { $client = new ClientMock([ 'baseUri' => '/', ]); $client->addTrustedCertificates('foo.txt'); $this->assertEquals("foo.txt", $client->curlSettings[CURLOPT_CAINFO]); } function testSetVerifyPeer() { $client = new ClientMock([ 'baseUri' => '/', ]); $client->setVerifyPeer(false); $this->assertEquals(false, $client->curlSettings[CURLOPT_SSL_VERIFYPEER]); } function testPropFind() { $client = new ClientMock([ 'baseUri' => '/', ]); $responseBody = <<<XML <?xml version="1.0"?> <multistatus xmlns="DAV:"> <response> <href>/foo</href> <propstat> <prop> <displayname>bar</displayname> </prop> <status>HTTP/1.1 200 OK</status> </propstat> </response> </multistatus> XML; $client->response = new Response(207, [], $responseBody); $result = $client->propfind('foo', ['{DAV:}displayname', '{urn:zim}gir']); $this->assertEquals(['{DAV:}displayname' => 'bar'], $result); $request = $client->request; $this->assertEquals('PROPFIND', $request->getMethod()); $this->assertEquals('/foo', $request->getUrl()); $this->assertEquals([ 'Depth' => ['0'], 'Content-Type' => ['application/xml'], ], $request->getHeaders()); } /** * @expectedException \Sabre\DAV\Exception */ function testPropFindError() { $client = new ClientMock([ 'baseUri' => '/', ]); $client->response = new Response(405, []); $client->propfind('foo', ['{DAV:}displayname', '{urn:zim}gir']); } function testPropFindDepth1() { $client = new ClientMock([ 'baseUri' => '/', ]); $responseBody = <<<XML <?xml version="1.0"?> <multistatus xmlns="DAV:"> <response> <href>/foo</href> <propstat> <prop> <displayname>bar</displayname> </prop> <status>HTTP/1.1 200 OK</status> </propstat> </response> </multistatus> XML; $client->response = new Response(207, [], $responseBody); $result = $client->propfind('foo', ['{DAV:}displayname', '{urn:zim}gir'], 1); $this->assertEquals([ '/foo' => [ '{DAV:}displayname' => 'bar' ], ], $result); $request = $client->request; $this->assertEquals('PROPFIND', $request->getMethod()); $this->assertEquals('/foo', $request->getUrl()); $this->assertEquals([ 'Depth' => ['1'], 'Content-Type' => ['application/xml'], ], $request->getHeaders()); } function testPropPatch() { $client = new ClientMock([ 'baseUri' => '/', ]); $responseBody = <<<XML <?xml version="1.0"?> <multistatus xmlns="DAV:"> <response> <href>/foo</href> <propstat> <prop> <displayname>bar</displayname> </prop> <status>HTTP/1.1 200 OK</status> </propstat> </response> </multistatus> XML; $client->response = new Response(207, [], $responseBody); $result = $client->propPatch('foo', ['{DAV:}displayname' => 'hi', '{urn:zim}gir' => null], 1); $request = $client->request; $this->assertEquals('PROPPATCH', $request->getMethod()); $this->assertEquals('/foo', $request->getUrl()); $this->assertEquals([ 'Content-Type' => ['application/xml'], ], $request->getHeaders()); } function testOPTIONS() { $client = new ClientMock([ 'baseUri' => '/', ]); $client->response = new Response(207, [ 'DAV' => 'calendar-access, extended-mkcol', ]); $result = $client->options(); $this->assertEquals( ['calendar-access', 'extended-mkcol'], $result ); $request = $client->request; $this->assertEquals('OPTIONS', $request->getMethod()); $this->assertEquals('/', $request->getUrl()); $this->assertEquals([ ], $request->getHeaders()); } } �����������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/CopyTest.php�������������������������������������������������������0000664�0000000�0000000�00000001347�12670356754�0020556�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; class CopyTest extends \PHPUnit_Framework_TestCase { public function setUp() { \Sabre\TestUtil::clearTempDir(); } /** * This test makes sure that a path like /foo cannot be copied into a path * like /foo/bar/ * * @expectedException \Sabre\DAV\Exception\Conflict */ public function testCopyIntoSubPath() { $dir = new FS\Directory(SABRE_TEMPDIR); $server = new Server($dir); $dir->createDirectory('foo'); $request = new HTTP\Request('COPY','/foo', [ 'Destination' => '/foo/bar', ]); $response = new HTTP\ResponseMock(); $server->invokeMethod($request, $response); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Exception/���������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0020224�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Exception/LockedTest.php�������������������������������������������0000664�0000000�0000000�00000002754�12670356754�0023006�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Exception; use Sabre\DAV, DOMDocument; class LockedTest extends \PHPUnit_Framework_TestCase { function testSerialize() { $dom = new DOMDocument('1.0'); $dom->formatOutput = true; $root = $dom->createElement('d:root'); $dom->appendChild($root); $root->setAttribute('xmlns:d','DAV:'); $lockInfo = new DAV\Locks\LockInfo(); $lockInfo->uri = '/foo'; $locked = new Locked($lockInfo); $locked->serialize(new DAV\Server(), $root); $output = $dom->saveXML(); $expected = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:lock-token-submitted xmlns:d="DAV:"> <d:href>/foo</d:href> </d:lock-token-submitted> </d:root> '; $this->assertEquals($expected, $output); } function testSerializeAmpersand() { $dom = new DOMDocument('1.0'); $dom->formatOutput = true; $root = $dom->createElement('d:root'); $dom->appendChild($root); $root->setAttribute('xmlns:d','DAV:'); $lockInfo = new DAV\Locks\LockInfo(); $lockInfo->uri = '/foo&bar'; $locked = new Locked($lockInfo); $locked->serialize(new DAV\Server(), $root); $output = $dom->saveXML(); $expected = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:lock-token-submitted xmlns:d="DAV:"> <d:href>/foo&bar</d:href> </d:lock-token-submitted> </d:root> '; $this->assertEquals($expected, $output); } } ��������������������sabre-dav-2.1.10/tests/Sabre/DAV/Exception/PaymentRequiredTest.php����������������������������������0000664�0000000�0000000�00000000357�12670356754�0024720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Exception; class PaymentRequiredTest extends \PHPUnit_Framework_TestCase { function testGetHTTPCode() { $ex = new PaymentRequired(); $this->assertEquals(402, $ex->getHTTPCode()); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Exception/ServiceUnavailableTest.php�������������������������������0000664�0000000�0000000�00000000365�12670356754�0025345�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Exception; class ServiceUnavailableTest extends \PHPUnit_Framework_TestCase { function testGetHTTPCode() { $ex = new ServiceUnavailable(); $this->assertEquals(503, $ex->getHTTPCode()); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Exception/TooManyMatchesTest.php�����������������������������������0000664�0000000�0000000�00000001275�12670356754�0024475�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Exception; use Sabre\DAV, DOMDocument; class TooManyMatchesTest extends \PHPUnit_Framework_TestCase { function testSerialize() { $dom = new DOMDocument('1.0'); $dom->formatOutput = true; $root = $dom->createElement('d:root'); $dom->appendChild($root); $root->setAttribute('xmlns:d','DAV:'); $locked = new TooManyMatches(); $locked->serialize(new DAV\Server(), $root); $output = $dom->saveXML(); $expected = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:number-of-matches-within-limits xmlns:d="DAV:"/> </d:root> '; $this->assertEquals($expected, $output); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ExceptionTest.php��������������������������������������������������0000664�0000000�0000000�00000001070�12670356754�0021573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class ExceptionTest extends \PHPUnit_Framework_TestCase { function testStatus() { $e = new Exception(); $this->assertEquals(500,$e->getHTTPCode()); } function testExceptionStatuses() { $c = array( 'Sabre\\DAV\\Exception\\NotAuthenticated' => 401, 'Sabre\\DAV\\Exception\\InsufficientStorage' => 507, ); foreach($c as $class=>$status) { $obj = new $class(); $this->assertEquals($status, $obj->getHTTPCode()); } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/FSExt/�������������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0017257�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/FSExt/FileTest.php�������������������������������������������������0000664�0000000�0000000�00000003731�12670356754�0021513�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\FSExt; use Sabre\DAV; require_once 'Sabre/TestUtil.php'; class FileTest extends \PHPUnit_Framework_TestCase { function setUp() { file_put_contents(SABRE_TEMPDIR . '/file.txt', 'Contents'); } function tearDown() { \Sabre\TestUtil::clearTempDir(); } function testPut() { $file = new File(SABRE_TEMPDIR . '/file.txt'); $result = $file->put('New contents'); $this->assertEquals('New contents',file_get_contents(SABRE_TEMPDIR . '/file.txt')); $this->assertEquals('"' . md5('New contents') . '"', $result); } function testRange() { $file = new File(SABRE_TEMPDIR . '/file.txt'); $file->put('0000000'); $file->patch('111', 2, 3); $this->assertEquals('0001110',file_get_contents(SABRE_TEMPDIR . '/file.txt')); } function testRangeStream() { $stream = fopen('php://memory','r+'); fwrite($stream, "222"); rewind($stream); $file = new File(SABRE_TEMPDIR . '/file.txt'); $file->put('0000000'); $file->patch($stream, 2, 3); $this->assertEquals('0002220',file_get_contents(SABRE_TEMPDIR . '/file.txt')); } function testGet() { $file = new File(SABRE_TEMPDIR . '/file.txt'); $this->assertEquals('Contents',stream_get_contents($file->get())); } function testDelete() { $file = new File(SABRE_TEMPDIR . '/file.txt'); $file->delete(); $this->assertFalse(file_exists(SABRE_TEMPDIR . '/file.txt')); } function testGetETag() { $file = new File(SABRE_TEMPDIR . '/file.txt'); $this->assertEquals('"' . md5('Contents') . '"',$file->getETag()); } function testGetContentType() { $file = new File(SABRE_TEMPDIR . '/file.txt'); $this->assertNull($file->getContentType()); } function testGetSize() { $file = new File(SABRE_TEMPDIR . '/file.txt'); $this->assertEquals(8,$file->getSize()); } } ���������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/FSExt/NodeTest.php�������������������������������������������������0000664�0000000�0000000�00000013274�12670356754�0021524�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\FSExt; use Sabre\DAV; use Sabre\DAV\PropPatch; require_once 'Sabre/TestUtil.php'; class NodeTest extends \PHPUnit_Framework_TestCase { function setUp() { mkdir(SABRE_TEMPDIR . '/dir'); file_put_contents(SABRE_TEMPDIR . '/dir/file.txt', 'Contents'); file_put_contents(SABRE_TEMPDIR . '/dir/file2.txt', 'Contents2'); } function tearDown() { \Sabre\TestUtil::clearTempDir(); } function testUpdateProperties() { $file = new File(SABRE_TEMPDIR . '/dir/file.txt'); $properties = array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test2' => 'bar', ); $propPatch = new PropPatch($properties); $file->propPatch($propPatch); $propPatch->commit(); $getProperties = $file->getProperties(array_keys($properties)); $this->assertEquals($properties, $getProperties); } /** * @depends testUpdateProperties */ function testUpdatePropertiesAgain() { $file = new File(SABRE_TEMPDIR . '/dir/file.txt'); $mutations = array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test2' => 'bar', ); $propPatch = new PropPatch($mutations); $file->propPatch($propPatch); $result = $propPatch->commit(); $this->assertEquals(true, $result); $mutations = array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test3' => 'baz', ); $propPatch = new PropPatch($mutations); $file->propPatch($propPatch); $result = $propPatch->commit(); $this->assertEquals(true, $result); } /** * @depends testUpdateProperties */ function testUpdatePropertiesDelete() { $file = new File(SABRE_TEMPDIR . '/dir/file.txt'); $mutations = array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test2' => 'bar', ); $propPatch = new PropPatch($mutations); $file->propPatch($propPatch); $result = $propPatch->commit(); $this->assertEquals(true, $result); $mutations = array( '{http://sabredav.org/NS/2010}test1' => null, '{http://sabredav.org/NS/2010}test3' => null ); $propPatch = new PropPatch($mutations); $file->propPatch($propPatch); $result = $propPatch->commit(); $this->assertEquals(true, $result); $properties = $file->getProperties(array('http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); $this->assertEquals(array( '{http://sabredav.org/NS/2010}test2' => 'bar', ), $properties); } /** * @depends testUpdateProperties */ function testUpdatePropertiesMove() { $file = new File(SABRE_TEMPDIR . '/dir/file.txt'); $mutations = array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test2' => 'bar', ); $propPatch = new PropPatch($mutations); $file->propPatch($propPatch); $result = $propPatch->commit(); $this->assertEquals(true, $result); $properties = $file->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); $this->assertEquals(array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test2' => 'bar', ), $properties); // Renaming $file->setName('file3.txt'); $this->assertFalse(file_exists(SABRE_TEMPDIR . '/dir/file.txt')); $this->assertTrue(file_exists(SABRE_TEMPDIR . '/dir/file3.txt')); $this->assertEquals('file3.txt',$file->getName()); $newFile = new File(SABRE_TEMPDIR . '/dir/file3.txt'); $this->assertEquals('file3.txt',$newFile->getName()); $properties = $newFile->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); $this->assertEquals(array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test2' => 'bar', ), $properties); } /** * @depends testUpdatePropertiesMove */ function testUpdatePropertiesDeleteBleed() { $file = new File(SABRE_TEMPDIR . '/dir/file.txt'); $mutations = array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test2' => 'bar', ); $propPatch = new PropPatch($mutations); $file->propPatch($propPatch); $result = $propPatch->commit(); $this->assertEquals(true, $result); $properties = $file->getProperties(array('{http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); $this->assertEquals(array( '{http://sabredav.org/NS/2010}test1' => 'foo', '{http://sabredav.org/NS/2010}test2' => 'bar', ), $properties); // Deleting $file->delete(); $this->assertFalse(file_exists(SABRE_TEMPDIR . '/dir/file.txt')); // Creating it again file_put_contents(SABRE_TEMPDIR . '/dir/file.txt','New Contents'); $file = new File(SABRE_TEMPDIR . '/dir/file.txt'); $properties = $file->getProperties(array('http://sabredav.org/NS/2010}test1','{http://sabredav.org/NS/2010}test2','{http://sabredav.org/NS/2010}test3')); $this->assertEquals(array(), $properties); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/FSExt/ServerTest.php�����������������������������������������������0000664�0000000�0000000�00000017316�12670356754�0022106�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\FSExt; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAV/AbstractServer.php'; class ServerTest extends DAV\AbstractServer{ protected function getRootNode() { return new Directory($this->tempDir); } function testGet() { $request = new HTTP\Request('GET', '/test.txt'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(200, $this->response->getStatus(), 'Invalid status code received.'); $this->assertEquals([ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' .md5_file($this->tempDir . '/test.txt') . '"'], ], $this->response->getHeaders() ); $this->assertEquals('Test contents', stream_get_contents($this->response->body)); } function testHEAD() { $request = new HTTP\Request('HEAD', '/test.txt'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals([ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' . md5_file($this->tempDir . '/test.txt') . '"'], ], $this->response->getHeaders() ); $this->assertEquals(200,$this->response->status); $this->assertEquals('', $this->response->body); } function testPut() { $request = new HTTP\Request('PUT', '/testput.txt'); $request->setBody('Testing new file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals([ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Length' => [0], 'ETag' => ['"' . md5('Testing new file') . '"'], ], $this->response->getHeaders()); $this->assertEquals(201, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertEquals('Testing new file',file_get_contents($this->tempDir . '/testput.txt')); } function testPutAlreadyExists() { $request = new HTTP\Request('PUT', '/test.txt', ['If-None-Match' => '*']); $request->setBody('Testing new file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals([ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ],$this->response->getHeaders()); $this->assertEquals(412, $this->response->status); $this->assertNotEquals('Testing new file',file_get_contents($this->tempDir . '/test.txt')); } function testMkcol() { $request = new HTTP\Request('MKCOL', '/testcol'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals([ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Length' => ['0'], ],$this->response->getHeaders()); $this->assertEquals(201, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertTrue(is_dir($this->tempDir . '/testcol')); } function testPutUpdate() { $request = new HTTP\Request('PUT', '/test.txt'); $request->setBody('Testing updated file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals('0', $this->response->getHeader('Content-Length')); $this->assertEquals(204, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertEquals('Testing updated file',file_get_contents($this->tempDir . '/test.txt')); } function testDelete() { $request = new HTTP\Request('DELETE', '/test.txt'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals([ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Length' => ['0'], ],$this->response->getHeaders()); $this->assertEquals(204, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertFalse(file_exists($this->tempDir . '/test.txt')); } function testDeleteDirectory() { mkdir($this->tempDir.'/testcol'); file_put_contents($this->tempDir.'/testcol/test.txt','Hi! I\'m a file with a short lifespan'); $request = new HTTP\Request('DELETE', '/testcol'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals([ 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Length' => ['0'], ],$this->response->getHeaders()); $this->assertEquals(204, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertFalse(file_exists($this->tempDir . '/col')); } function testOptions() { $request = new HTTP\Request('OPTIONS', '/'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals([ 'DAV' => ['1, 3, extended-mkcol'], 'MS-Author-Via' => ['DAV'], 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'], 'Accept-Ranges' => ['bytes'], 'Content-Length' => ['0'], 'X-Sabre-Version'=> [DAV\Version::VERSION], ], $this->response->getHeaders()); $this->assertEquals(200, $this->response->status); $this->assertEquals('', $this->response->body); } function testMove() { mkdir($this->tempDir.'/testcol'); $request = new HTTP\Request('MOVE', '/test.txt', ['Destination' => '/testcol/test2.txt']); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(201, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertEquals([ 'Content-Length' => ['0'], 'X-Sabre-Version'=> [DAV\Version::VERSION], ],$this->response->getHeaders()); $this->assertTrue( is_file($this->tempDir . '/testcol/test2.txt') ); } /** * This test checks if it's possible to move a non-FSExt collection into a * FSExt collection. * * The moveInto function *should* ignore the object and let sabredav itself * execute the slow move. */ function testMoveOtherObject() { mkdir($this->tempDir.'/tree1'); mkdir($this->tempDir.'/tree2'); $tree = new DAV\Tree(new DAV\SimpleCollection('root', [ new DAV\FS\Directory($this->tempDir . '/tree1'), new DAV\FSExt\Directory($this->tempDir . '/tree2'), ])); $this->server->tree = $tree; $request = new HTTP\Request('MOVE', '/tree1', ['Destination' => '/tree2/tree1']); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(201, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertEquals([ 'Content-Length' => ['0'], 'X-Sabre-Version'=> [DAV\Version::VERSION], ],$this->response->getHeaders()); $this->assertTrue( is_dir($this->tempDir . '/tree2/tree1') ); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/GetIfConditionsTest.php��������������������������������������������0000664�0000000�0000000�00000022070�12670356754�0022670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; require_once 'Sabre/DAV/AbstractServer.php'; class GetIfConditionsTest extends AbstractServer { function testNoConditions() { $request = new HTTP\Request(); $conditions = $this->server->getIfConditions($request); $this->assertEquals(array(),$conditions); } function testLockToken() { $request = new HTTP\Request('GET', '/path/', ['If' => '(<opaquelocktoken:token1>)']); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => 'path', 'tokens' => array( array( 'negate' => false, 'token' => 'opaquelocktoken:token1', 'etag' => '', ), ), ), ); $this->assertEquals($compare,$conditions); } function testNotLockToken() { $serverVars = array( 'HTTP_IF' => '(Not <opaquelocktoken:token1>)', 'REQUEST_URI' => '/bla' ); $request = HTTP\Sapi::createFromServerArray($serverVars); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => 'bla', 'tokens' => array( array( 'negate' => true, 'token' => 'opaquelocktoken:token1', 'etag' => '', ), ), ), ); $this->assertEquals($compare,$conditions); } function testLockTokenUrl() { $serverVars = array( 'HTTP_IF' => '<http://www.example.com/> (<opaquelocktoken:token1>)', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => '', 'tokens' => array( array( 'negate' => false, 'token' => 'opaquelocktoken:token1', 'etag' => '', ), ), ), ); $this->assertEquals($compare,$conditions); } function test2LockTokens() { $serverVars = array( 'HTTP_IF' => '(<opaquelocktoken:token1>) (Not <opaquelocktoken:token2>)', 'REQUEST_URI' => '/bla', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => 'bla', 'tokens' => array( array( 'negate' => false, 'token' => 'opaquelocktoken:token1', 'etag' => '', ), array( 'negate' => true, 'token' => 'opaquelocktoken:token2', 'etag' => '', ), ), ), ); $this->assertEquals($compare,$conditions); } function test2UriLockTokens() { $serverVars = array( 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1>) <http://www.example.org/node2> (Not <opaquelocktoken:token2>)', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => 'node1', 'tokens' => array( array( 'negate' => false, 'token' => 'opaquelocktoken:token1', 'etag' => '', ), ), ), array( 'uri' => 'node2', 'tokens' => array( array( 'negate' => true, 'token' => 'opaquelocktoken:token2', 'etag' => '', ), ), ), ); $this->assertEquals($compare,$conditions); } function test2UriMultiLockTokens() { $serverVars = array( 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1>) (<opaquelocktoken:token2>) <http://www.example.org/node2> (Not <opaquelocktoken:token3>)', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => 'node1', 'tokens' => array( array( 'negate' => false, 'token' => 'opaquelocktoken:token1', 'etag' => '', ), array( 'negate' => false, 'token' => 'opaquelocktoken:token2', 'etag' => '', ), ), ), array( 'uri' => 'node2', 'tokens' => array( array( 'negate' => true, 'token' => 'opaquelocktoken:token3', 'etag' => '', ), ), ), ); $this->assertEquals($compare,$conditions); } function testEtag() { $serverVars = array( 'HTTP_IF' => '(["etag1"])', 'REQUEST_URI' => '/foo', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => 'foo', 'tokens' => array( array( 'negate' => false, 'token' => '', 'etag' => '"etag1"', ), ), ), ); $this->assertEquals($compare,$conditions); } function test2Etags() { $serverVars = array( 'HTTP_IF' => '<http://www.example.org/> (["etag1"]) (["etag2"])', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => '', 'tokens' => array( array( 'negate' => false, 'token' => '', 'etag' => '"etag1"', ), array( 'negate' => false, 'token' => '', 'etag' => '"etag2"', ), ), ), ); $this->assertEquals($compare,$conditions); } function testComplexIf() { $serverVars = array( 'HTTP_IF' => '<http://www.example.org/node1> (<opaquelocktoken:token1> ["etag1"]) ' . '(Not <opaquelocktoken:token2>) (["etag2"]) <http://www.example.org/node2> ' . '(<opaquelocktoken:token3>) (Not <opaquelocktoken:token4>) (["etag3"])', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $conditions = $this->server->getIfConditions($request); $compare = array( array( 'uri' => 'node1', 'tokens' => array( array( 'negate' => false, 'token' => 'opaquelocktoken:token1', 'etag' => '"etag1"', ), array( 'negate' => true, 'token' => 'opaquelocktoken:token2', 'etag' => '', ), array( 'negate' => false, 'token' => '', 'etag' => '"etag2"', ), ), ), array( 'uri' => 'node2', 'tokens' => array( array( 'negate' => false, 'token' => 'opaquelocktoken:token3', 'etag' => '', ), array( 'negate' => true, 'token' => 'opaquelocktoken:token4', 'etag' => '', ), array( 'negate' => false, 'token' => '', 'etag' => '"etag3"', ), ), ), ); $this->assertEquals($compare,$conditions); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/HTTPPreferParsingTest.php������������������������������������������0000664�0000000�0000000�00000011646�12670356754�0023116�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; class HTTPPReferParsingTest extends \Sabre\DAVServerTest { function testParseSimple() { $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_PREFER' => 'return-asynch', )); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals(array( 'return-asynch' => true, 'return-minimal' => false, 'return-representation' => false, 'strict' => false, 'lenient' => false, 'wait' => null, ), $server->getHTTPPrefer()); } function testParseValue() { $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_PREFER' => 'wait=10', )); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals(array( 'return-asynch' => false, 'return-minimal' => false, 'return-representation' => false, 'strict' => false, 'lenient' => false, 'wait' => 10, ), $server->getHTTPPrefer()); } function testParseMultiple() { $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_PREFER' => 'return-minimal, strict,lenient', )); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals(array( 'return-asynch' => false, 'return-minimal' => true, 'return-representation' => false, 'strict' => true, 'lenient' => true, 'wait' => null, ), $server->getHTTPPrefer()); } function testParseWeirdValue() { $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_PREFER' => 'BOOOH', )); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals(array( 'strict' => false, 'lenient' => false, 'wait' => null, 'return-asynch' => false, 'return-minimal' => false, 'return-representation' => false, ), $server->getHTTPPrefer()); } function testBrief() { $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_BRIEF' => 't', )); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals(array( 'strict' => false, 'lenient' => false, 'wait' => null, 'return-asynch' => false, 'return-minimal' => true, 'return-representation' => false, ), $server->getHTTPPrefer()); } /** * propfindMinimal * * @return void */ function testpropfindMinimal() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PROPFIND', 'REQUEST_URI' => '/', 'HTTP_PREFER' => 'return-minimal', )); $request->setBody(<<<BLA <?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:something /> <d:resourcetype /> </d:prop> </d:propfind> BLA ); $response = $this->request($request); $this->assertTrue(strpos($response->body, 'resourcetype')!==false); $this->assertTrue(strpos($response->body, 'something')===false); } function testproppatchMinimal() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PROPPATCH', 'REQUEST_URI' => '/', 'HTTP_PREFER' => 'return-minimal', )); $request->setBody(<<<BLA <?xml version="1.0"?> <d:proppatch xmlns:d="DAV:"> <d:set> <d:prop> <d:something>nope!</d:something> </d:prop> </d:set> </d:proppatch> BLA ); $this->server->on('propPatch', function($path, PropPatch $propPatch) { $propPatch->handle('{DAV:}something', function($props) { return true; }); }); $response = $this->request($request); $this->assertEquals(0, strlen($response->body), 'Expected empty body: ' . $response->body); $this->assertEquals(204, $response->status); } function testproppatchMinimalError() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PROPPATCH', 'REQUEST_URI' => '/', 'HTTP_PREFER' => 'return-minimal', )); $request->setBody(<<<BLA <?xml version="1.0"?> <d:proppatch xmlns:d="DAV:"> <d:set> <d:prop> <d:something>nope!</d:something> </d:prop> </d:set> </d:proppatch> BLA ); $response = $this->request($request); $this->assertEquals(207, $response->status); $this->assertTrue(strpos($response->body, 'something')!==false); $this->assertTrue(strpos($response->body, '403 Forbidden')!==false); } } ������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/HttpDeleteTest.php�������������������������������������������������0000664�0000000�0000000�00000006062�12670356754�0021705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\DAVServerTest; use Sabre\HTTP; /** * Tests related to the PUT request. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class HttpDeleteTest extends DAVServerTest { /** * Sets up the DAV tree. * * @return void */ public function setUpTree() { $this->tree = new Mock\Collection('root', [ 'file1' => 'foo', 'dir' => [ 'subfile' => 'bar', 'subfile2' => 'baz', ], ]); } /** * A successful DELETE */ public function testDelete() { $request = new HTTP\Request('DELETE', '/file1'); $response = $this->request($request); $this->assertEquals( 204, $response->getStatus(), "Incorrect status code. Response body: " . $response->getBodyAsString() ); $this->assertEquals( [ 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ], $response->getHeaders() ); } /** * Deleting a Directory */ public function testDeleteDirectory() { $request = new HTTP\Request('DELETE', '/dir'); $response = $this->request($request); $this->assertEquals( 204, $response->getStatus(), "Incorrect status code. Response body: " . $response->getBodyAsString() ); $this->assertEquals( [ 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ], $response->getHeaders() ); } /** * DELETE on a node that does not exist */ public function testDeleteNotFound() { $request = new HTTP\Request('DELETE', '/file2'); $response = $this->request($request); $this->assertEquals( 404, $response->getStatus(), "Incorrect status code. Response body: " . $response->getBodyAsString() ); } /** * DELETE with preconditions */ public function testDeletePreconditions() { $request = new HTTP\Request('DELETE', '/file1', [ 'If-Match' => '"' . md5('foo') . '"', ]); $response = $this->request($request); $this->assertEquals( 204, $response->getStatus(), "Incorrect status code. Response body: " . $response->getBodyAsString() ); } /** * DELETE with incorrect preconditions */ public function testDeletePreconditionsFailed() { $request = new HTTP\Request('DELETE', '/file1', [ 'If-Match' => '"' . md5('bar') . '"', ]); $response = $this->request($request); $this->assertEquals( 412, $response->getStatus(), "Incorrect status code. Response body: " . $response->getBodyAsString() ); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/HttpMoveTest.php���������������������������������������������������0000664�0000000�0000000�00000007300�12670356754�0021405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\DAVServerTest; use Sabre\HTTP; /** * Tests related to the MOVE request. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class HttpMoveTest extends DAVServerTest { /** * Sets up the DAV tree. * * @return void */ function setUpTree() { $this->tree = new Mock\Collection('root', [ 'file1' => 'content1', 'file2' => 'content2', ]); } function testMoveToSelf() { $request = new HTTP\Request('MOVE', '/file1', [ 'Destination' => '/file1' ]); $response = $this->request($request); $this->assertEquals(403, $response->getStatus()); $this->assertEquals('content1', $this->tree->getChild('file1')->get()); } function testMove() { $request = new HTTP\Request('MOVE', '/file1', [ 'Destination' => '/file3' ]); $response = $this->request($request); $this->assertEquals(201, $response->getStatus(), print_r($response,true)); $this->assertEquals('content1', $this->tree->getChild('file3')->get()); $this->assertFalse($this->tree->childExists('file1')); } function testMoveToExisting() { $request = new HTTP\Request('MOVE', '/file1', [ 'Destination' => '/file2' ]); $response = $this->request($request); $this->assertEquals(204, $response->getStatus(), print_r($response,true)); $this->assertEquals('content1', $this->tree->getChild('file2')->get()); $this->assertFalse($this->tree->childExists('file1')); } function testMoveToExistingOverwriteT() { $request = new HTTP\Request('MOVE', '/file1', [ 'Destination' => '/file2', 'Overwrite' => 'T', ]); $response = $this->request($request); $this->assertEquals(204, $response->getStatus(), print_r($response,true)); $this->assertEquals('content1', $this->tree->getChild('file2')->get()); $this->assertFalse($this->tree->childExists('file1')); } function testMoveToExistingOverwriteF() { $request = new HTTP\Request('MOVE', '/file1', [ 'Destination' => '/file2', 'Overwrite' => 'F', ]); $response = $this->request($request); $this->assertEquals(412, $response->getStatus(), print_r($response,true)); $this->assertEquals('content1', $this->tree->getChild('file1')->get()); $this->assertEquals('content2', $this->tree->getChild('file2')->get()); $this->assertTrue($this->tree->childExists('file1')); $this->assertTrue($this->tree->childExists('file2')); } /** * If we MOVE to an existing file, but a plugin prevents the original from * being deleted, we need to make sure that the server does not delete * the destination. */ function testMoveToExistingBlockedDeleteSource() { $this->server->on('beforeUnbind', function($path) { if ($path==='file1') { throw new \Sabre\DAV\Exception\Forbidden('uh oh'); } }); $request = new HTTP\Request('MOVE', '/file1', [ 'Destination' => '/file2' ]); $response = $this->request($request); $this->assertEquals(403, $response->getStatus(), print_r($response,true)); $this->assertEquals('content1', $this->tree->getChild('file1')->get()); $this->assertEquals('content2', $this->tree->getChild('file2')->get()); $this->assertTrue($this->tree->childExists('file1')); $this->assertTrue($this->tree->childExists('file2')); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/HttpPutTest.php����������������������������������������������������0000664�0000000�0000000�00000020126�12670356754�0021250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\DAVServerTest; use Sabre\HTTP; /** * Tests related to the PUT request. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class HttpPutTest extends DAVServerTest { /** * Sets up the DAV tree. * * @return void */ public function setUpTree() { $this->tree = new Mock\Collection('root', [ 'file1' => 'foo', ]); } /** * A successful PUT of a new file. */ public function testPut() { $request = new HTTP\Request('PUT', '/file2', [], 'hello'); $response = $this->request($request); $this->assertEquals(201, $response->getStatus(), 'Incorrect status code received. Full response body:' . $response->getBodyAsString()); $this->assertEquals( 'hello', $this->server->tree->getNodeForPath('file2')->get() ); $this->assertEquals( [ 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], 'ETag' => ['"' . md5('hello') . '"'] ], $response->getHeaders() ); } /** * A successful PUT on an existing file. * * @depends testPut */ public function testPutExisting() { $request = new HTTP\Request('PUT', '/file1', [], 'bar'); $response = $this->request($request); $this->assertEquals(204, $response->getStatus()); $this->assertEquals( 'bar', $this->server->tree->getNodeForPath('file1')->get() ); $this->assertEquals( [ 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], 'ETag' => ['"' . md5('bar') . '"'] ], $response->getHeaders() ); } /** * PUT on existing file with If-Match: * * * @depends testPutExisting */ public function testPutExistingIfMatchStar() { $request = new HTTP\Request( 'PUT', '/file1', ['If-Match' => '*'], 'hello' ); $response = $this->request($request); $this->assertEquals(204, $response->getStatus()); $this->assertEquals( 'hello', $this->server->tree->getNodeForPath('file1')->get() ); $this->assertEquals( [ 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], 'ETag' => ['"' . md5('hello') . '"'] ], $response->getHeaders() ); } /** * PUT on existing file with If-Match: with a correct etag * * @depends testPutExisting */ public function testPutExistingIfMatchCorrect() { $request = new HTTP\Request( 'PUT', '/file1', ['If-Match' => '"' . md5('foo') . '"'], 'hello' ); $response = $this->request($request); $this->assertEquals(204, $response->status); $this->assertEquals( 'hello', $this->server->tree->getNodeForPath('file1')->get() ); $this->assertEquals( [ 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], 'ETag' => ['"' . md5('hello') . '"'], ], $response->getHeaders() ); } /** * PUT with Content-Range should be rejected. * * @depends testPut */ public function testPutContentRange() { $request = new HTTP\Request( 'PUT', '/file2', ['Content-Range' => 'bytes/100-200'], 'hello' ); $response = $this->request($request); $this->assertEquals(400, $response->getStatus()); } /** * PUT on non-existing file with If-None-Match: * should work. * * @depends testPut */ public function testPutIfNoneMatchStar() { $request = new HTTP\Request( 'PUT', '/file2', ['If-None-Match' => '*'], 'hello' ); $response = $this->request($request); $this->assertEquals(201, $response->getStatus()); $this->assertEquals( 'hello', $this->server->tree->getNodeForPath('file2')->get() ); $this->assertEquals( [ 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], 'ETag' => ['"' . md5('hello') . '"'] ], $response->getHeaders() ); } /** * PUT on non-existing file with If-Match: * should fail. * * @depends testPut */ public function testPutIfMatchStar() { $request = new HTTP\Request( 'PUT', '/file2', ['If-Match' => '*'], 'hello' ); $response = $this->request($request); $this->assertEquals(412, $response->getStatus()); } /** * PUT on existing file with If-None-Match: * should fail. * * @depends testPut */ public function testPutExistingIfNoneMatchStar() { $request = new HTTP\Request( 'PUT', '/file1', ['If-None-Match' => '*'], 'hello' ); $request->setBody('hello'); $response = $this->request($request); $this->assertEquals(412, $response->getStatus()); } /** * PUT thats created in a non-collection should be rejected. * * @depends testPut */ public function testPutNoParent() { $request = new HTTP\Request( 'PUT', '/file1/file2', [], 'hello' ); $response = $this->request($request); $this->assertEquals(409, $response->getStatus()); } /** * Finder may sometimes make a request, which gets its content-body * stripped. We can't always prevent this from happening, but in some cases * we can detected this and return an error instead. * * @depends testPut */ public function testFinderPutSuccess() { $request = new HTTP\Request( 'PUT', '/file2', ['X-Expected-Entity-Length' => '5'], 'hello' ); $response = $this->request($request); $this->assertEquals(201, $response->getStatus()); $this->assertEquals( 'hello', $this->server->tree->getNodeForPath('file2')->get() ); $this->assertEquals( [ 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], 'ETag' => ['"' . md5('hello') . '"'], ], $response->getHeaders() ); } /** * Same as the last one, but in this case we're mimicing a failed request. * * @depends testFinderPutSuccess */ public function testFinderPutFail() { $request = new HTTP\Request( 'PUT', '/file2', ['X-Expected-Entity-Length' => '5'], '' ); $response = $this->request($request); $this->assertEquals(403, $response->getStatus()); } /** * Plugins can intercept PUT. We need to make sure that works. * * @depends testPut */ public function testPutIntercept() { $this->server->on('beforeBind', function($uri) { $this->server->httpResponse->setStatus(418); return false; }); $request = new HTTP\Request('PUT', '/file2', [], 'hello'); $response = $this->request($request); $this->assertEquals(418, $response->getStatus(), 'Incorrect status code received. Full response body: ' .$response->getBodyAsString()); $this->assertFalse( $this->server->tree->nodeExists('file2') ); $this->assertEquals([ 'X-Sabre-Version' => [Version::VERSION], ], $response->getHeaders()); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Issue33Test.php����������������������������������������������������0000664�0000000�0000000�00000005156�12670356754�0021104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/TestUtil.php'; class Issue33Test extends \PHPUnit_Framework_TestCase { function setUp() { \Sabre\TestUtil::clearTempDir(); } function testCopyMoveInfo() { $bar = new SimpleCollection('bar'); $root = new SimpleCollection('webdav',array($bar)); $server = new Server($root); $server->setBaseUri('/webdav/'); $serverVars = array( 'REQUEST_URI' => '/webdav/bar', 'HTTP_DESTINATION' => 'http://dev2.tribalos.com/webdav/%C3%A0fo%C3%B3', 'HTTP_OVERWRITE' => 'F', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $server->httpRequest = $request; $info = $server->getCopyAndMoveInfo($request); $this->assertEquals('%C3%A0fo%C3%B3', urlencode($info['destination'])); $this->assertFalse($info['destinationExists']); $this->assertFalse($info['destinationNode']); } function testTreeMove() { mkdir(SABRE_TEMPDIR . '/issue33'); $dir = new FS\Directory(SABRE_TEMPDIR . '/issue33'); $dir->createDirectory('bar'); $tree = new Tree($dir); $tree->move('bar',urldecode('%C3%A0fo%C3%B3')); $node = $tree->getNodeForPath(urldecode('%C3%A0fo%C3%B3')); $this->assertEquals(urldecode('%C3%A0fo%C3%B3'),$node->getName()); } function testDirName() { $dirname1 = 'bar'; $dirname2 = urlencode('%C3%A0fo%C3%B3');; $this->assertTrue(dirname($dirname1)==dirname($dirname2)); } /** * @depends testTreeMove * @depends testCopyMoveInfo */ function testEverything() { // Request object $serverVars = array( 'REQUEST_METHOD' => 'MOVE', 'REQUEST_URI' => '/webdav/bar', 'HTTP_DESTINATION' => 'http://dev2.tribalos.com/webdav/%C3%A0fo%C3%B3', 'HTTP_OVERWRITE' => 'F', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody(''); $response = new HTTP\ResponseMock(); // Server setup mkdir(SABRE_TEMPDIR . '/issue33'); $dir = new FS\Directory(SABRE_TEMPDIR . '/issue33'); $dir->createDirectory('bar'); $tree = new Tree($dir); $server = new Server($tree); $server->setBaseUri('/webdav/'); $server->httpRequest = $request; $server->httpResponse = $response; $server->sapi = new HTTP\SapiMock(); $server->exec(); $this->assertTrue(file_exists(SABRE_TEMPDIR . '/issue33/' . urldecode('%C3%A0fo%C3%B3'))); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/�������������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0017341�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/Backend/�����������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0020670�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/Backend/AbstractTest.php�������������������������������������0000664�0000000�0000000�00000011454�12670356754�0024011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks\Backend; use Sabre\DAV; abstract class AbstractTest extends \PHPUnit_Framework_TestCase { /** * @abstract * @return AbstractBackend */ abstract function getBackend(); function testSetup() { $backend = $this->getBackend(); $this->assertInstanceOf('Sabre\\DAV\\Locks\\Backend\\AbstractBackend', $backend); } /** * @depends testSetup */ function testGetLocks() { $backend = $this->getBackend(); $lock = new DAV\Locks\LockInfo(); $lock->owner = 'Sinterklaas'; $lock->timeout = 60; $lock->created = time(); $lock->token = 'MY-UNIQUE-TOKEN'; $lock->uri ='someuri'; $this->assertTrue($backend->lock('someuri', $lock)); $locks = $backend->getLocks('someuri', false); $this->assertEquals(1,count($locks)); $this->assertEquals('Sinterklaas',$locks[0]->owner); $this->assertEquals('someuri',$locks[0]->uri); } /** * @depends testGetLocks */ function testGetLocksParent() { $backend = $this->getBackend(); $lock = new DAV\Locks\LockInfo(); $lock->owner = 'Sinterklaas'; $lock->timeout = 60; $lock->created = time(); $lock->depth = DAV\Server::DEPTH_INFINITY; $lock->token = 'MY-UNIQUE-TOKEN'; $this->assertTrue($backend->lock('someuri', $lock)); $locks = $backend->getLocks('someuri/child', false); $this->assertEquals(1,count($locks)); $this->assertEquals('Sinterklaas',$locks[0]->owner); $this->assertEquals('someuri',$locks[0]->uri); } /** * @depends testGetLocks */ function testGetLocksParentDepth0() { $backend = $this->getBackend(); $lock = new DAV\Locks\LockInfo(); $lock->owner = 'Sinterklaas'; $lock->timeout = 60; $lock->created = time(); $lock->depth = 0; $lock->token = 'MY-UNIQUE-TOKEN'; $this->assertTrue($backend->lock('someuri', $lock)); $locks = $backend->getLocks('someuri/child', false); $this->assertEquals(0,count($locks)); } function testGetLocksChildren() { $backend = $this->getBackend(); $lock = new DAV\Locks\LockInfo(); $lock->owner = 'Sinterklaas'; $lock->timeout = 60; $lock->created = time(); $lock->depth = 0; $lock->token = 'MY-UNIQUE-TOKEN'; $this->assertTrue($backend->lock('someuri/child', $lock)); $locks = $backend->getLocks('someuri/child', false); $this->assertEquals(1,count($locks)); $locks = $backend->getLocks('someuri', false); $this->assertEquals(0,count($locks)); $locks = $backend->getLocks('someuri', true); $this->assertEquals(1,count($locks)); } /** * @depends testGetLocks */ function testLockRefresh() { $backend = $this->getBackend(); $lock = new DAV\Locks\LockInfo(); $lock->owner = 'Sinterklaas'; $lock->timeout = 60; $lock->created = time(); $lock->token = 'MY-UNIQUE-TOKEN'; $this->assertTrue($backend->lock('someuri', $lock)); /* Second time */ $lock->owner = 'Santa Clause'; $this->assertTrue($backend->lock('someuri', $lock)); $locks = $backend->getLocks('someuri', false); $this->assertEquals(1,count($locks)); $this->assertEquals('Santa Clause',$locks[0]->owner); $this->assertEquals('someuri',$locks[0]->uri); } /** * @depends testGetLocks */ function testUnlock() { $backend = $this->getBackend(); $lock = new DAV\Locks\LockInfo(); $lock->owner = 'Sinterklaas'; $lock->timeout = 60; $lock->created = time(); $lock->token = 'MY-UNIQUE-TOKEN'; $this->assertTrue($backend->lock('someuri', $lock)); $locks = $backend->getLocks('someuri', false); $this->assertEquals(1,count($locks)); $this->assertTrue($backend->unlock('someuri',$lock)); $locks = $backend->getLocks('someuri', false); $this->assertEquals(0,count($locks)); } /** * @depends testUnlock */ function testUnlockUnknownToken() { $backend = $this->getBackend(); $lock = new DAV\Locks\LockInfo(); $lock->owner = 'Sinterklaas'; $lock->timeout = 60; $lock->created = time(); $lock->token = 'MY-UNIQUE-TOKEN'; $this->assertTrue($backend->lock('someuri', $lock)); $locks = $backend->getLocks('someuri', false); $this->assertEquals(1,count($locks)); $lock->token = 'SOME-OTHER-TOKEN'; $this->assertFalse($backend->unlock('someuri',$lock)); $locks = $backend->getLocks('someuri', false); $this->assertEquals(1,count($locks)); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/Backend/FSTest.php�������������������������������������������0000664�0000000�0000000�00000001271�12670356754�0022552�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks\Backend; require_once 'Sabre/TestUtil.php'; class FSTest extends AbstractTest { function getBackend() { \Sabre\TestUtil::clearTempDir(); mkdir(SABRE_TEMPDIR . '/locks'); $backend = new FS(SABRE_TEMPDIR . '/locks/'); return $backend; } function tearDown() { \Sabre\TestUtil::clearTempDir(); } function testGetLocksChildren() { // We're skipping this test. This doesn't work, and it will // never. The class is deprecated anyway. // // We need to assert something though, so phpunit won't fail in strict // mode. $this->assertTrue(true); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/Backend/FileTest.php�����������������������������������������0000664�0000000�0000000�00000000544�12670356754�0023123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks\Backend; require_once 'Sabre/TestUtil.php'; class FileTest extends AbstractTest { function getBackend() { \Sabre\TestUtil::clearTempDir(); $backend = new File(SABRE_TEMPDIR . '/lockdb'); return $backend; } function tearDown() { \Sabre\TestUtil::clearTempDir(); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/Backend/Mock.php���������������������������������������������0000664�0000000�0000000�00000006037�12670356754�0022300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks\Backend; use Sabre\DAV\Locks\LockInfo; /** * Locks Mock backend. * * This backend stores lock information in memory. Mainly useful for testing. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class Mock extends AbstractBackend { /** * Returns a list of Sabre\DAV\Locks\LockInfo objects * * This method should return all the locks for a particular uri, including * locks that might be set on a parent uri. * * If returnChildLocks is set to true, this method should also look for * any locks in the subtree of the uri for locks. * * @param string $uri * @param bool $returnChildLocks * @return array */ public function getLocks($uri, $returnChildLocks) { $newLocks = array(); $locks = $this->getData(); foreach($locks as $lock) { if ($lock->uri === $uri || //deep locks on parents ($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) || // locks on children ($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) { $newLocks[] = $lock; } } // Checking if we can remove any of these locks foreach($newLocks as $k=>$lock) { if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); } return $newLocks; } /** * Locks a uri * * @param string $uri * @param LockInfo $lockInfo * @return bool */ public function lock($uri, LockInfo $lockInfo) { // We're making the lock timeout 30 minutes $lockInfo->timeout = 1800; $lockInfo->created = time(); $lockInfo->uri = $uri; $locks = $this->getData(); foreach($locks as $k=>$lock) { if ( ($lock->token == $lockInfo->token) || (time() > $lock->timeout + $lock->created) ) { unset($locks[$k]); } } $locks[] = $lockInfo; $this->putData($locks); return true; } /** * Removes a lock from a uri * * @param string $uri * @param LockInfo $lockInfo * @return bool */ public function unlock($uri, LockInfo $lockInfo) { $locks = $this->getData(); foreach($locks as $k=>$lock) { if ($lock->token == $lockInfo->token) { unset($locks[$k]); $this->putData($locks); return true; } } return false; } protected $data = []; /** * Loads the lockdata from the filesystem. * * @return array */ protected function getData() { return $this->data; } /** * Saves the lockdata * * @param array $newData * @return void */ protected function putData(array $newData) { $this->data = $newData; } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/Backend/PDOMySQLTest.php�������������������������������������0000664�0000000�0000000�00000001366�12670356754�0023557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks\Backend; require_once 'Sabre/TestUtil.php'; class PDOMySQLTest extends AbstractTest { function getBackend() { if (!SABRE_HASMYSQL) $this->markTestSkipped('MySQL driver is not available, or it was not properly configured'); $pdo = \Sabre\TestUtil::getMySQLDB(); if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database'); $pdo->query('DROP TABLE IF EXISTS locks;'); $pdo->query(" 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 );"); $backend = new PDO($pdo); return $backend; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/Backend/PDOTest.php������������������������������������������0000664�0000000�0000000�00000001505�12670356754�0022664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks\Backend; require_once 'Sabre/TestUtil.php'; require_once 'Sabre/DAV/Locks/Backend/AbstractTest.php'; class PDOTest extends AbstractTest { function getBackend() { if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available'); \Sabre\TestUtil::clearTempDir(); mkdir(SABRE_TEMPDIR . '/pdolocks'); $pdo = new \PDO('sqlite:' . SABRE_TEMPDIR . '/pdolocks/db.sqlite'); $pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); $pdo->query('CREATE TABLE locks ( id integer primary key asc, owner text, timeout text, created integer, token text, scope integer, depth integer, uri text)'); $backend = new PDO($pdo); return $backend; } function tearDown() { \Sabre\TestUtil::clearTempDir(); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/MSWordTest.php�����������������������������������������������0000664�0000000�0000000�00000006524�12670356754�0022074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks; use Sabre\HTTP; use Sabre\DAV; require_once 'Sabre/HTTP/ResponseMock.php'; require_once 'Sabre/TestUtil.php'; class MSWordTest extends \PHPUnit_Framework_TestCase { function testLockEtc() { mkdir(SABRE_TEMPDIR . '/mstest'); $tree = new DAV\FS\Directory(SABRE_TEMPDIR . '/mstest'); $server = new DAV\Server($tree); $server->debugExceptions = true; $locksBackend = new Backend\File(SABRE_TEMPDIR . '/locksdb'); $locksPlugin = new Plugin($locksBackend); $server->addPlugin($locksPlugin); $response1 = new HTTP\ResponseMock(); $server->httpRequest = $this->getLockRequest(); $server->httpResponse = $response1; $server->sapi = new HTTP\SapiMock(); $server->exec(); $this->assertEquals(201, $server->httpResponse->getStatus(), 'Full response body:' . $response1->getBodyAsString()); $this->assertTrue(!!$server->httpResponse->getHeaders('Lock-Token')); $lockToken = $server->httpResponse->getHeader('Lock-Token'); //sleep(10); $response2 = new HTTP\ResponseMock(); $server->httpRequest = $this->getLockRequest2(); $server->httpResponse = $response2; $server->exec(); $this->assertEquals(201, $server->httpResponse->status); $this->assertTrue(!!$server->httpResponse->getHeaders('Lock-Token')); //sleep(10); $response3 = new HTTP\ResponseMock(); $server->httpRequest = $this->getPutRequest($lockToken); $server->httpResponse = $response3; $server->exec(); $this->assertEquals(204, $server->httpResponse->status); } function tearDown() { \Sabre\TestUtil::clearTempDir(); } function getLockRequest() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'LOCK', 'HTTP_CONTENT_TYPE' => 'application/xml', 'HTTP_TIMEOUT' => 'Second-3600', 'REQUEST_URI' => '/Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', )); $request->setBody('<D:lockinfo xmlns:D="DAV:"> <D:lockscope> <D:exclusive /> </D:lockscope> <D:locktype> <D:write /> </D:locktype> <D:owner> <D:href>PC-Vista\User</D:href> </D:owner> </D:lockinfo>'); return $request; } function getLockRequest2() { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'LOCK', 'HTTP_CONTENT_TYPE' => 'application/xml', 'HTTP_TIMEOUT' => 'Second-3600', 'REQUEST_URI' => '/~$Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', )); $request->setBody('<D:lockinfo xmlns:D="DAV:"> <D:lockscope> <D:exclusive /> </D:lockscope> <D:locktype> <D:write /> </D:locktype> <D:owner> <D:href>PC-Vista\User</D:href> </D:owner> </D:lockinfo>'); return $request; } function getPutRequest($lockToken) { $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/Nouveau%20Microsoft%20Office%20Excel%20Worksheet.xlsx', 'HTTP_IF' => 'If: ('.$lockToken.')', )); $request->setBody('FAKE BODY'); return $request; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/Plugin2Test.php����������������������������������������������0000664�0000000�0000000�00000003061�12670356754�0022232�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks; use Sabre\HTTP\Request; class Plugin2Test extends \Sabre\DAVServerTest { public $setupLocks = true; function setUpTree() { $this->tree = new \Sabre\DAV\FS\Directory(SABRE_TEMPDIR); } function tearDown() { \Sabre\TestUtil::clearTempDir(); } /** * This test first creates a file with LOCK and then deletes it. * * After deleting the file, the lock should no longer be in the lock * backend. * * Reported in ticket #487 */ function testUnlockAfterDelete() { $body = '<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> </D:lockinfo>'; $request = new Request( 'LOCK', '/file.txt', [], $body ); $response = $this->request($request); $this->assertEquals(201, $response->getStatus(), $response->getBodyAsString()); $this->assertEquals( 1, count($this->locksBackend->getLocks('file.txt', true)) ); $request = new Request( 'DELETE', '/file.txt', [ 'If' => '(' . $response->getHeader('Lock-Token') . ')', ] ); $response = $this->request($request); $this->assertEquals(204, $response->getStatus(), $response->getBodyAsString()); $this->assertEquals( 0, count($this->locksBackend->getLocks('file.txt', true)) ); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Locks/PluginTest.php�����������������������������������������������0000664�0000000�0000000�00000101471�12670356754�0022154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Locks; use Sabre\HTTP; use Sabre\DAV; require_once 'Sabre/DAV/AbstractServer.php'; class PluginTest extends DAV\AbstractServer { /** * @var Sabre\DAV\Locks\Plugin */ protected $locksPlugin; function setUp() { parent::setUp(); $locksBackend = new Backend\File(SABRE_TEMPDIR . '/locksdb'); $locksPlugin = new Plugin($locksBackend); $this->server->addPlugin($locksPlugin); $this->locksPlugin = $locksPlugin; } function testGetFeatures() { $this->assertEquals(array(2),$this->locksPlugin->getFeatures()); } function testGetHTTPMethods() { $this->assertEquals(array('LOCK','UNLOCK'),$this->locksPlugin->getHTTPMethods('')); } function testGetHTTPMethodsNoBackend() { $locksPlugin = new Plugin(); $this->server->addPlugin($locksPlugin); $this->assertEquals(array(),$locksPlugin->getHTTPMethods('')); } function testLockNoBody() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody(''); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $this->response->getHeaders() ); $this->assertEquals(400, $this->response->status); } function testLock() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status,'Got an incorrect status back. Response body: ' . $this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $elements = array( '/d:prop', '/d:prop/d:lockdiscovery', '/d:prop/d:lockdiscovery/d:activelock', '/d:prop/d:lockdiscovery/d:activelock/d:locktype', '/d:prop/d:lockdiscovery/d:activelock/d:lockroot', '/d:prop/d:lockdiscovery/d:activelock/d:lockroot/d:href', '/d:prop/d:lockdiscovery/d:activelock/d:locktype/d:write', '/d:prop/d:lockdiscovery/d:activelock/d:lockscope', '/d:prop/d:lockdiscovery/d:activelock/d:lockscope/d:exclusive', '/d:prop/d:lockdiscovery/d:activelock/d:depth', '/d:prop/d:lockdiscovery/d:activelock/d:owner', '/d:prop/d:lockdiscovery/d:activelock/d:timeout', '/d:prop/d:lockdiscovery/d:activelock/d:locktoken', '/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href', ); foreach($elements as $elem) { $data = $xml->xpath($elem); $this->assertEquals(1,count($data),'We expected 1 match for the xpath expression "' . $elem . '". ' . count($data) . ' were found. Full response body: ' . $this->response->body); } $depth = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:depth'); $this->assertEquals('infinity',(string)$depth[0]); $token = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href'); $this->assertEquals($this->response->getHeader('Lock-Token'),'<' . (string)$token[0] . '>','Token in response body didn\'t match token in response header.'); } /** * @depends testLock */ function testDoubleLock() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->response = new HTTP\ResponseMock(); $this->server->httpResponse = $this->response; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertEquals(423, $this->response->status, 'Full response: ' . $this->response->body); } /** * @depends testLock */ function testLockRefresh() { $request = new HTTP\Request('LOCK', '/test.txt'); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $lockToken = $this->response->getHeader('Lock-Token'); $this->response = new HTTP\ResponseMock(); $this->server->httpResponse = $this->response; $request = new HTTP\Request('LOCK', '/test.txt', ['If' => '(' . $lockToken . ')' ]); $request->setBody(''); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertEquals(200, $this->response->status,'We received an incorrect status code. Full response body: ' . $this->response->getBody()); } /** * @depends testLock */ function testLockNoFile() { $serverVars = array( 'REQUEST_URI' => '/notfound.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(201, $this->response->status); } /** * @depends testLock */ function testUnlockNoToken() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'UNLOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $this->response->getHeaders() ); $this->assertEquals(400, $this->response->status); } /** * @depends testLock */ function testUnlockBadToken() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'UNLOCK', 'HTTP_LOCK_TOKEN' => '<opaquelocktoken:blablabla>', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $this->response->getHeaders() ); $this->assertEquals(409, $this->response->status, 'Got an incorrect status code. Full response body: ' . $this->response->body); } /** * @depends testLock */ function testLockPutNoToken() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'PUT', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('newbody'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(423, $this->response->status); } /** * @depends testLock */ function testUnlock() { $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ]); $this->server->httpRequest = $request; $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->invokeMethod($request, $this->server->httpResponse); $lockToken = $this->server->httpResponse->getHeader('Lock-Token'); $serverVars = array( 'HTTP_LOCK_TOKEN' => $lockToken, 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'UNLOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->httpResponse = new HTTP\ResponseMock(); $this->server->invokeMethod($request, $this->server->httpResponse); $this->assertEquals(204,$this->server->httpResponse->status,'Got an incorrect status code. Full response body: ' . $this->response->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Length' => ['0'], ), $this->server->httpResponse->getHeaders() ); } /** * @depends testLock */ function testUnlockWindowsBug() { $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ]); $this->server->httpRequest = $request; $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->invokeMethod($request, $this->server->httpResponse); $lockToken = $this->server->httpResponse->getHeader('Lock-Token'); // See Issue 123 $lockToken = trim($lockToken,'<>'); $serverVars = array( 'HTTP_LOCK_TOKEN' => $lockToken, 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'UNLOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->httpResponse = new HTTP\ResponseMock(); $this->server->invokeMethod($request, $this->server->httpResponse); $this->assertEquals(204, $this->server->httpResponse->status,'Got an incorrect status code. Full response body: ' . $this->response->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Length' => ['0'], ), $this->server->httpResponse->getHeaders() ); } /** * @depends testLock */ function testLockRetainOwner() { $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ]); $this->server->httpRequest = $request; $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner>Evert</D:owner> </D:lockinfo>'); $this->server->invokeMethod($request, $this->server->httpResponse); $lockToken = $this->server->httpResponse->getHeader('Lock-Token'); $locks = $this->locksPlugin->getLocks('test.txt'); $this->assertEquals(1,count($locks)); $this->assertEquals('Evert',$locks[0]->owner); } /** * @depends testLock */ function testLockPutBadToken() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'PUT', 'HTTP_IF' => '(<opaquelocktoken:token1>)', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('newbody'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); // $this->assertEquals('412 Precondition failed',$this->response->status); $this->assertEquals(423, $this->response->status); } /** * @depends testLock */ function testLockDeleteParent() { $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/dir', 'REQUEST_METHOD' => 'DELETE', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(423, $this->response->status); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); } /** * @depends testLock */ function testLockDeleteSucceed() { $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'DELETE', 'HTTP_IF' => '(' . $this->response->getHeader('Lock-Token') . ')', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(204, $this->response->status); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); } /** * @depends testLock */ function testLockCopyLockSource() { $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/dir/child2.txt', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(201, $this->response->status,'Copy must succeed if only the source is locked, but not the destination'); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); } /** * @depends testLock */ function testLockCopyLockDestination() { $serverVars = array( 'REQUEST_URI' => '/dir/child2.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(201, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/dir/child2.txt', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(423, $this->response->status,'Copy must succeed if only the source is locked, but not the destination'); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); } /** * @depends testLock */ function testLockMoveLockSourceLocked() { $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'MOVE', 'HTTP_DESTINATION' => '/dir/child2.txt', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(423, $this->response->status,'Copy must succeed if only the source is locked, but not the destination'); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); } /** * @depends testLock */ function testLockMoveLockSourceSucceed() { $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'MOVE', 'HTTP_DESTINATION' => '/dir/child2.txt', 'HTTP_IF' => '(' . $this->response->getHeader('Lock-Token') . ')', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(201, $this->response->status,'A valid lock-token was provided for the source, so this MOVE operation must succeed. Full response body: ' . $this->response->body); } /** * @depends testLock */ function testLockMoveLockDestination() { $serverVars = array( 'REQUEST_URI' => '/dir/child2.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(201, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'MOVE', 'HTTP_DESTINATION' => '/dir/child2.txt', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(423, $this->response->status,'Copy must succeed if only the source is locked, but not the destination'); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); } /** * @depends testLock */ function testLockMoveLockParent() { $serverVars = array( 'REQUEST_URI' => '/dir', 'REQUEST_METHOD' => 'LOCK', 'HTTP_DEPTH' => 'infinite', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200,$this->response->status); $serverVars = array( 'REQUEST_URI' => '/dir/child.txt', 'REQUEST_METHOD' => 'MOVE', 'HTTP_DESTINATION' => '/dir/child2.txt', 'HTTP_IF' => '</dir> (' . $this->response->getHeader('Lock-Token') . ')', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(201, $this->response->status,'We locked the parent of both the source and destination, but the move didn\'t succeed.'); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); } /** * @depends testLock */ function testLockPutGoodToken() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(200, $this->response->status); $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'PUT', 'HTTP_IF' => '('.$this->response->getHeader('Lock-Token').')', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('newbody'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals(204, $this->response->status); } function testPutWithIncorrectETag() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'PUT', 'HTTP_IF' => '(["etag1"])', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('newbody'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(412, $this->response->status); } /** * @depends testPutWithIncorrectETag */ function testPutWithCorrectETag() { // We need an etag-enabled file node. $tree = new DAV\Tree(new DAV\FSExt\Directory(SABRE_TEMPDIR)); $this->server->tree = $tree; $etag = md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')); $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'PUT', 'HTTP_IF' => '(["'.$etag.'"])', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('newbody'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(204, $this->response->status, 'Incorrect status received. Full response body:' . $this->response->body); } function testDeleteWithETagOnCollection() { $serverVars = array( 'REQUEST_URI' => '/dir', 'REQUEST_METHOD' => 'DELETE', 'HTTP_IF' => '(["etag1"])', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(412, $this->response->status); } function testGetTimeoutHeader() { $request = HTTP\Sapi::createFromServerArray(array( 'HTTP_TIMEOUT' => 'second-100', )); $this->server->httpRequest = $request; $this->assertEquals(100, $this->locksPlugin->getTimeoutHeader()); } function testGetTimeoutHeaderTwoItems() { $request = HTTP\Sapi::createFromServerArray(array( 'HTTP_TIMEOUT' => 'second-5, infinite', )); $this->server->httpRequest = $request; $this->assertEquals(5, $this->locksPlugin->getTimeoutHeader()); } function testGetTimeoutHeaderInfinite() { $request = HTTP\Sapi::createFromServerArray(array( 'HTTP_TIMEOUT' => 'infinite, second-5', )); $this->server->httpRequest = $request; $this->assertEquals(LockInfo::TIMEOUT_INFINITE, $this->locksPlugin->getTimeoutHeader()); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testGetTimeoutHeaderInvalid() { $request = HTTP\Sapi::createFromServerArray(array( 'HTTP_TIMEOUT' => 'yourmom', )); $this->server->httpRequest = $request; $this->locksPlugin->getTimeoutHeader(); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Mock/��������������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0017157�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Mock/Collection.php������������������������������������������������0000664�0000000�0000000�00000007515�12670356754�0021773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Mock; use Sabre\DAV; /** * Mock Collection. * * This collection quickly allows you to create trees of nodes. * Children are specified as an array. * * Every key a filename, every array value is either: * * an array, for a sub-collection * * a string, for a file * * An instance of \Sabre\DAV\INode. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class Collection extends DAV\Collection { protected $name; protected $children; protected $parent; /** * Creates the object * * @param string $name * @param array $children * @return void */ public function __construct($name, array $children = array(), Collection $parent = null) { $this->name = $name; foreach($children as $key=>$value) { if (is_string($value)) { $this->children[] = new File($key, $value, $this); } elseif (is_array($value)) { $this->children[] = new Collection($key, $value, $this); } elseif ($value instanceof \Sabre\DAV\INode) { $this->children[] = $value; } else { throw new \InvalidArgumentException('Unknown value passed in $children'); } } $this->parent = $parent; } /** * Returns the name of the node. * * This is used to generate the url. * * @return string */ public function getName() { return $this->name; } /** * Creates a new file in the directory * * Data will either be supplied as a stream resource, or in certain cases * as a string. Keep in mind that you may have to support either. * * After successful creation of the file, you may choose to return the ETag * of the new file here. * * The returned ETag must be surrounded by double-quotes (The quotes should * be part of the actual string). * * If you cannot accurately determine the ETag, you should not return it. * If you don't store the file exactly as-is (you're transforming it * somehow) you should also not return an ETag. * * This means that if a subsequent GET to this new file does not exactly * return the same contents of what was submitted here, you are strongly * recommended to omit the ETag. * * @param string $name Name of the file * @param resource|string $data Initial payload * @return null|string */ public function createFile($name, $data = null) { if (is_resource($data)) { $data = stream_get_contents($data); } $this->children[] = new File($name, $data, $this); return '"' . md5($data) . '"'; } /** * Creates a new subdirectory * * @param string $name * @return void */ public function createDirectory($name) { $this->children[] = new Collection($name); } /** * Returns an array with all the child nodes * * @return \Sabre\DAV\INode[] */ public function getChildren() { return $this->children; } /** * Removes a childnode from this node. * * @param string $name * @return void */ public function deleteChild($name) { foreach($this->children as $key=>$value) { if ($value->getName() == $name) { unset($this->children[$key]); return; } } } /** * Deletes this collection and all its children,. * * @return void */ public function delete() { foreach($this->getChildren() as $child) { $this->deleteChild($child->getName()); } $this->parent->deleteChild($this->getName()); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Mock/File.php������������������������������������������������������0000664�0000000�0000000�00000005560�12670356754�0020555�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Mock; use Sabre\DAV; /** * Mock File * * See the Collection in this directory for more details. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class File extends DAV\File { protected $name; protected $contents; protected $parent; /** * Creates the object * * @param string $name * @param array $children * @return void */ public function __construct($name, $contents, Collection $parent) { $this->name = $name; $this->put($contents); $this->parent = $parent; } /** * Returns the name of the node. * * This is used to generate the url. * * @return string */ public function getName() { return $this->name; } /** * Changes the name of the node. * * @return void */ function setName($name) { $this->name = $name; } /** * Updates the data * * The data argument is a readable stream resource. * * After a succesful put operation, you may choose to return an ETag. The * etag must always be surrounded by double-quotes. These quotes must * appear in the actual string you're returning. * * Clients may use the ETag from a PUT request to later on make sure that * when they update the file, the contents haven't changed in the mean * time. * * If you don't plan to store the file byte-by-byte, and you return a * different object on a subsequent GET you are strongly recommended to not * return an ETag, and just return null. * * @param resource $data * @return string|null */ public function put($data) { if (is_resource($data)) { $data = stream_get_contents($data); } $this->contents = $data; return '"' . md5($data) . '"'; } /** * Returns the data * * This method may either return a string or a readable stream resource * * @return mixed */ public function get() { return $this->contents; } /** * Returns the ETag for a file * * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. * * Return null if the ETag can not effectively be determined * * @return void */ public function getETag() { return '"' . md5($this->contents) . '"'; } /** * Returns the size of the node, in bytes * * @return int */ public function getSize() { return strlen($this->contents); } /** * Delete the node * * @return void */ public function delete() { $this->parent->deleteChild($this->name); } } ������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Mock/PropertiesCollection.php��������������������������������������0000664�0000000�0000000�00000005110�12670356754�0024035�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Mock; use Sabre\DAV\IProperties, Sabre\DAV\PropPatch; /** * A node specifically for testing property-related operations * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class PropertiesCollection extends Collection implements IProperties { public $failMode = false; public $properties; /** * Creates the object * * @param string $name * @param array $children * @param array $properties * @return void */ public function __construct($name, array $children, array $properties = []) { parent::__construct($name, $children, null); $this->properties = $properties; } /** * Updates properties on this node. * * This method received a PropPatch object, which contains all the * information about the update. * * To update specific properties, call the 'handle' method on this object. * Read the PropPatch documentation for more information. * * @param array $mutations * @return bool|array */ public function propPatch(PropPatch $proppatch) { $proppatch->handleRemaining(function($updateProperties) { switch($this->failMode) { case 'updatepropsfalse' : return false; case 'updatepropsarray' : $r = []; foreach($updateProperties as $k=>$v) $r[$k] = 402; return $r; case 'updatepropsobj' : return new \STDClass(); } }); } /** * 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. * * Note that it's fine to liberally give properties back, instead of * conforming to the list of requested properties. * The Server class will filter out the extra. * * @param array $properties * @return array */ public function getProperties($requestedProperties) { $returnedProperties = array(); foreach($requestedProperties as $requestedProperty) { if (isset($this->properties[$requestedProperty])) { $returnedProperties[$requestedProperty] = $this->properties[$requestedProperty]; } } return $returnedProperties; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Mount/�������������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0017370�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Mount/PluginTest.php�����������������������������������������������0000664�0000000�0000000�00000003270�12670356754�0022201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Mount; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAV/AbstractServer.php'; class PluginTest extends DAV\AbstractServer { function setUp() { parent::setUp(); $this->server->addPlugin(new Plugin()); } function testPassThrough() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(501, $this->response->status,'We expected GET to not be implemented for Directories. Response body: ' . $this->response->body); } function testMountResponse() { $serverVars = array( 'REQUEST_URI' => '/?mount', 'REQUEST_METHOD' => 'GET', 'QUERY_STRING' => 'mount', 'HTTP_HOST' => 'example.org', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(200, $this->response->status); $xml = simplexml_load_string($this->response->body); $this->assertInstanceOf('SimpleXMLElement',$xml, 'Response was not a valid xml document. The list of errors:' . print_r(libxml_get_errors(),true) . '. xml body: ' . $this->response->body . '. What type we got: ' . gettype($xml) . ' class, if object: ' . get_class($xml)); $xml->registerXPathNamespace('dm','http://purl.org/NET/webdav/mount'); $url = $xml->xpath('//dm:url'); $this->assertEquals('http://example.org/',(string)$url[0]); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ObjectTreeTest.php�������������������������������������������������0000664�0000000�0000000�00000005450�12670356754�0021671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; require_once 'Sabre/TestUtil.php'; class ObjectTreeTest extends \PHPUnit_Framework_TestCase { protected $tree; function setup() { \Sabre\TestUtil::clearTempDir(); mkdir(SABRE_TEMPDIR . '/root'); mkdir(SABRE_TEMPDIR . '/root/subdir'); file_put_contents(SABRE_TEMPDIR . '/root/file.txt','contents'); file_put_contents(SABRE_TEMPDIR . '/root/subdir/subfile.txt','subcontents'); $rootNode = new FSExt\Directory(SABRE_TEMPDIR . '/root'); $this->tree = new Tree($rootNode); } function teardown() { \Sabre\TestUtil::clearTempDir(); } function testGetRootNode() { $root = $this->tree->getNodeForPath(''); $this->assertInstanceOf('Sabre\\DAV\\FSExt\\Directory',$root); } function testGetSubDir() { $root = $this->tree->getNodeForPath('subdir'); $this->assertInstanceOf('Sabre\\DAV\\FSExt\\Directory',$root); } function testCopyFile() { $this->tree->copy('file.txt','file2.txt'); $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/file2.txt')); $this->assertEquals('contents',file_get_contents(SABRE_TEMPDIR.'/root/file2.txt')); } /** * @depends testCopyFile */ function testCopyDirectory() { $this->tree->copy('subdir','subdir2'); $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir2')); $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir2/subfile.txt')); $this->assertEquals('subcontents',file_get_contents(SABRE_TEMPDIR.'/root/subdir2/subfile.txt')); } /** * @depends testCopyFile */ function testMoveFile() { $this->tree->move('file.txt','file2.txt'); $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/file2.txt')); $this->assertFalse(file_exists(SABRE_TEMPDIR.'/root/file.txt')); $this->assertEquals('contents',file_get_contents(SABRE_TEMPDIR.'/root/file2.txt')); } /** * @depends testMoveFile */ function testMoveFileNewParent() { $this->tree->move('file.txt','subdir/file2.txt'); $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir/file2.txt')); $this->assertFalse(file_exists(SABRE_TEMPDIR.'/root/file.txt')); $this->assertEquals('contents',file_get_contents(SABRE_TEMPDIR.'/root/subdir/file2.txt')); } /** * @depends testCopyDirectory */ function testMoveDirectory() { $this->tree->move('subdir','subdir2'); $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir2')); $this->assertTrue(file_exists(SABRE_TEMPDIR.'/root/subdir2/subfile.txt')); $this->assertFalse(file_exists(SABRE_TEMPDIR.'/root/subdir')); $this->assertEquals('subcontents',file_get_contents(SABRE_TEMPDIR.'/root/subdir2/subfile.txt')); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PartialUpdate/�����������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0021025�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PartialUpdate/FileMock.php�����������������������������������������0000664�0000000�0000000�00000002100�12670356754�0023220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\PartialUpdate; use Sabre\DAV; class FileMock implements IFile { protected $data = ''; function put($str) { if (is_resource($str)) { $str = stream_get_contents($str); } $this->data = $str; } function putRange($str,$start) { if (is_resource($str)) { $str = stream_get_contents($str); } $this->data = substr($this->data, 0, $start) . $str . substr($this->data, $start + strlen($str)); } function get() { return $this->data; } function getContentType() { return 'text/plain'; } function getSize() { return strlen($this->data); } function getETag() { return '"' . $this->data . '"'; } function delete() { throw new DAV\Exception\MethodNotAllowed(); } function setName($name) { throw new DAV\Exception\MethodNotAllowed(); } function getName() { return 'partial'; } function getLastModified() { return null; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PartialUpdate/PluginTest.php���������������������������������������0000664�0000000�0000000�00000007667�12670356754�0023654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\PartialUpdate; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAV/PartialUpdate/FileMock.php'; class PluginTest extends \Sabre\DAVServerTest { protected $node; protected $plugin; public function setUp() { $this->node = new FileMock(); $this->tree[] = $this->node; parent::setUp(); $this->plugin = new Plugin(); $this->server->addPlugin($this->plugin); } public function testInit() { $this->assertEquals('partialupdate', $this->plugin->getPluginName()); $this->assertEquals(array('sabredav-partialupdate'), $this->plugin->getFeatures()); $this->assertEquals(array( 'PATCH' ), $this->plugin->getHTTPMethods('partial')); $this->assertEquals(array( ), $this->plugin->getHTTPMethods('')); } public function testPatchNoRange() { $this->node->put('00000000'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PATCH', 'REQUEST_URI' => '/partial', )); $response = $this->request($request); $this->assertEquals(400, $response->status, 'Full response body:' . $response->body); } public function testPatchNotSupported() { $this->node->put('00000000'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PATCH', 'REQUEST_URI' => '/', 'X_UPDATE_RANGE' => '3-4', )); $request->setBody( '111' ); $response = $this->request($request); $this->assertEquals(405, $response->status, 'Full response body:' . $response->body); } public function testPatchNoContentType() { $this->node->put('00000000'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PATCH', 'REQUEST_URI' => '/partial', 'HTTP_X_UPDATE_RANGE' => 'bytes=3-4', )); $request->setBody( '111' ); $response = $this->request($request); $this->assertEquals(415, $response->status, 'Full response body:' . $response->body); } public function testPatchBadRange() { $this->node->put('00000000'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PATCH', 'REQUEST_URI' => '/partial', 'HTTP_X_UPDATE_RANGE' => 'bytes=3-4', 'HTTP_CONTENT_TYPE' => 'application/x-sabredav-partialupdate', )); $request->setBody( '111' ); $response = $this->request($request); $this->assertEquals(411, $response->status, 'Full response body:' . $response->body); } public function testPatchSuccess() { $this->node->put('00000000'); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PATCH', 'REQUEST_URI' => '/partial', 'HTTP_X_UPDATE_RANGE' => 'bytes=3-5', 'HTTP_CONTENT_TYPE' => 'application/x-sabredav-partialupdate', 'HTTP_CONTENT_LENGTH' => 3, )); $request->setBody( '111' ); $response = $this->request($request); $this->assertEquals(204, $response->status, 'Full response body:' . $response->body); $this->assertEquals('00011100', $this->node->get()); } public function testPatchNoEndRange() { $this->node->put('00000'); $request = new HTTP\Request('PATCH','/partial',[ 'X-Update-Range' => 'bytes=3-', 'Content-Type' => 'application/x-sabredav-partialupdate', 'Content-Length' => '3', ], '111'); $response = $this->request($request); $this->assertEquals(204, $response->getStatus(), 'Full response body:' . $response->getBodyAsString()); $this->assertEquals('00111', $this->node->get()); } } �������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PartialUpdate/SpecificationTest.php��������������������������������0000664�0000000�0000000�00000004451�12670356754�0025162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\PartialUpdate; use Sabre\DAV\FSExt\File; use Sabre\DAV\Server; use Sabre\HTTP; /** * This test is an end-to-end sabredav test that goes through all * the cases in the specification. * * See: http://sabre.io/dav/http-patch/ */ class SpecificationTest extends \PHPUnit_Framework_TestCase { protected $server; public function setUp() { $tree = array( new File(SABRE_TEMPDIR . '/foobar.txt') ); $server = new Server($tree); $server->debugExceptions = true; $server->addPlugin(new Plugin()); $tree[0]->put('1234567890'); $this->server = $server; } public function tearDown() { \Sabre\TestUtil::clearTempDir(); } /** * @dataProvider data */ public function testUpdateRange($headerValue, $httpStatus, $endResult, $contentLength = 4) { $headers = [ 'Content-Type' => 'application/x-sabredav-partialupdate', 'X-Update-Range' => $headerValue, ]; if ($contentLength) { $headers['Content-Length'] = (string)$contentLength; } $request = new HTTP\Request('PATCH', '/foobar.txt', $headers, '----'); $request->setBody('----'); $this->server->httpRequest = $request; $this->server->httpResponse = new HTTP\ResponseMock(); $this->server->sapi = new HTTP\SapiMock(); $this->server->exec(); $this->assertEquals($httpStatus, $this->server->httpResponse->status, 'Incorrect http status received: ' . $this->server->httpResponse->body); if (!is_null($endResult)) { $this->assertEquals($endResult, file_get_contents(SABRE_TEMPDIR . '/foobar.txt')); } } public function data() { return array( // Problems array('foo', 400, null), array('bytes=0-3', 411, null, 0), array('bytes=4-1', 416, null), array('bytes=0-3', 204, '----567890'), array('bytes=1-4', 204, '1----67890'), array('bytes=0-', 204, '----567890'), array('bytes=-4', 204, '123456----'), array('bytes=-2', 204, '12345678----'), array('bytes=2-', 204, '12----7890'), array('append', 204, '1234567890----'), ); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropFindTest.php���������������������������������������������������0000664�0000000�0000000�00000003673�12670356754�0021371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class PropFindTest extends \PHPUnit_Framework_TestCase { function testHandle() { $propFind = new PropFind('foo', ['{DAV:}displayname']); $propFind->handle('{DAV:}displayname', 'foobar'); $this->assertEquals([ 200 => ['{DAV:}displayname' => 'foobar'], 404 => [], ], $propFind->getResultForMultiStatus()); } function testHandleCallBack() { $propFind = new PropFind('foo', ['{DAV:}displayname']); $propFind->handle('{DAV:}displayname', function() { return 'foobar'; }); $this->assertEquals([ 200 => ['{DAV:}displayname' => 'foobar'], 404 => [], ], $propFind->getResultForMultiStatus()); } function testAllPropDefaults() { $propFind = new PropFind('foo', ['{DAV:}displayname'], 0, PropFind::ALLPROPS); $this->assertEquals([ 200 => [], ], $propFind->getResultForMultiStatus()); } function testSet() { $propFind = new PropFind('foo', ['{DAV:}displayname']); $propFind->set('{DAV:}displayname', 'bar'); $this->assertEquals([ 200 => ['{DAV:}displayname' => 'bar'], 404 => [], ], $propFind->getResultForMultiStatus()); } function testSetAllpropCustom() { $propFind = new PropFind('foo', ['{DAV:}displayname'], 0, PropFind::ALLPROPS); $propFind->set('{DAV:}customproperty', 'bar'); $this->assertEquals([ 200 => ['{DAV:}customproperty' => 'bar'], ], $propFind->getResultForMultiStatus()); } function testSetUnset() { $propFind = new PropFind('foo', ['{DAV:}displayname']); $propFind->set('{DAV:}displayname', 'bar'); $propFind->set('{DAV:}displayname', null); $this->assertEquals([ 200 => [], 404 => ['{DAV:}displayname' => null], ], $propFind->getResultForMultiStatus()); } } ���������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropPatchTest.php��������������������������������������������������0000664�0000000�0000000�00000022114�12670356754�0021537�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class PropPatchTest extends \PHPUnit_Framework_TestCase { protected $propPatch; public function setUp() { $this->propPatch = new PropPatch([ '{DAV:}displayname' => 'foo', ]); $this->assertEquals(['{DAV:}displayname' => 'foo'], $this->propPatch->getMutations()); } public function testHandleSingleSuccess() { $hasRan = false; $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { $hasRan = true; $this->assertEquals('foo', $value); return true; }); $this->assertTrue($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 200], $result); $this->assertTrue($hasRan); } public function testHandleSingleFail() { $hasRan = false; $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { $hasRan = true; $this->assertEquals('foo', $value); return false; }); $this->assertFalse($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 403], $result); $this->assertTrue($hasRan); } public function testHandleSingleCustomResult() { $hasRan = false; $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { $hasRan = true; $this->assertEquals('foo', $value); return 201; }); $this->assertTrue($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 201], $result); $this->assertTrue($hasRan); } public function testHandleSingleDeleteSuccess() { $hasRan = false; $this->propPatch = new PropPatch(['{DAV:}displayname' => null]); $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { $hasRan = true; $this->assertNull($value); return true; }); $this->assertTrue($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 204], $result); $this->assertTrue($hasRan); } public function testHandleNothing() { $hasRan = false; $this->propPatch->handle('{DAV:}foobar', function($value) use (&$hasRan) { $hasRan = true; }); $this->assertFalse($hasRan); } /** * @depends testHandleSingleSuccess */ public function testHandleRemaining() { $hasRan = false; $this->propPatch->handleRemaining(function($mutations) use (&$hasRan) { $hasRan = true; $this->assertEquals(['{DAV:}displayname' => 'foo'], $mutations); return true; }); $this->assertTrue($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 200], $result); $this->assertTrue($hasRan); } public function testHandleRemainingNothingToDo() { $hasRan = false; $this->propPatch->handle('{DAV:}displayname', function() {} ); $this->propPatch->handleRemaining(function($mutations) use (&$hasRan) { $hasRan = true; }); $this->assertFalse($hasRan); } public function testSetResultCode() { $this->propPatch->setResultCode('{DAV:}displayname', 201); $this->assertTrue($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 201], $result); } public function testSetResultCodeFail() { $this->propPatch->setResultCode('{DAV:}displayname', 402); $this->assertFalse($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 402], $result); } public function testSetRemainingResultCode() { $this->propPatch->setRemainingResultCode(204); $this->assertTrue($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 204], $result); } public function testCommitNoHandler() { $this->assertFalse($this->propPatch->commit()); $result = $this->propPatch->getResult(); $this->assertEquals(['{DAV:}displayname' => 403], $result); } public function testHandlerNotCalled() { $hasRan = false; $this->propPatch->setResultCode('{DAV:}displayname', 402); $this->propPatch->handle('{DAV:}displayname', function($value) use (&$hasRan) { $hasRan = true; }); $this->propPatch->commit(); // The handler is not supposed to have ran $this->assertFalse($hasRan); } public function testDependencyFail() { $propPatch = new PropPatch([ '{DAV:}a' => 'foo', '{DAV:}b' => 'bar', ]); $calledA = false; $calledB = false; $propPatch->handle('{DAV:}a', function() use (&$calledA) { $calledA = true; return false; }); $propPatch->handle('{DAV:}b', function() use (&$calledB) { $calledB = true; return false; }); $result = $propPatch->commit(); $this->assertTrue($calledA); $this->assertFalse($calledB); $this->assertFalse($result); $this->assertEquals([ '{DAV:}a' => 403, '{DAV:}b' => 424, ], $propPatch->getResult()); } /** * @expectedException \UnexpectedValueException */ public function testHandleSingleBrokenResult() { $propPatch = new PropPatch([ '{DAV:}a' => 'foo', ]); $calledA = false; $calledB = false; $propPatch->handle('{DAV:}a', function() use (&$calledA) { return []; }); $propPatch->commit(); } public function testHandleMultiValueSuccess() { $propPatch = new PropPatch([ '{DAV:}a' => 'foo', '{DAV:}b' => 'bar', '{DAV:}c' => null, ]); $calledA = false; $propPatch->handle(['{DAV:}a', '{DAV:}b', '{DAV:}c'], function($properties) use (&$calledA) { $calledA = true; $this->assertEquals([ '{DAV:}a' => 'foo', '{DAV:}b' => 'bar', '{DAV:}c' => null, ], $properties); return true; }); $result = $propPatch->commit(); $this->assertTrue($calledA); $this->assertTrue($result); $this->assertEquals([ '{DAV:}a' => 200, '{DAV:}b' => 200, '{DAV:}c' => 204, ], $propPatch->getResult()); } public function testHandleMultiValueFail() { $propPatch = new PropPatch([ '{DAV:}a' => 'foo', '{DAV:}b' => 'bar', '{DAV:}c' => null, ]); $calledA = false; $propPatch->handle(['{DAV:}a', '{DAV:}b', '{DAV:}c'], function($properties) use (&$calledA) { $calledA = true; $this->assertEquals([ '{DAV:}a' => 'foo', '{DAV:}b' => 'bar', '{DAV:}c' => null, ], $properties); return false; }); $result = $propPatch->commit(); $this->assertTrue($calledA); $this->assertFalse($result); $this->assertEquals([ '{DAV:}a' => 403, '{DAV:}b' => 403, '{DAV:}c' => 403, ], $propPatch->getResult()); } public function testHandleMultiValueCustomResult() { $propPatch = new PropPatch([ '{DAV:}a' => 'foo', '{DAV:}b' => 'bar', '{DAV:}c' => null, ]); $calledA = false; $propPatch->handle(['{DAV:}a', '{DAV:}b', '{DAV:}c'], function($properties) use (&$calledA) { $calledA = true; $this->assertEquals([ '{DAV:}a' => 'foo', '{DAV:}b' => 'bar', '{DAV:}c' => null, ], $properties); return [ '{DAV:}a' => 201, '{DAV:}b' => 204, ]; return false; }); $result = $propPatch->commit(); $this->assertTrue($calledA); $this->assertFalse($result); $this->assertEquals([ '{DAV:}a' => 201, '{DAV:}b' => 204, '{DAV:}c' => 500, ], $propPatch->getResult()); } /** * @expectedException \UnexpectedValueException */ public function testHandleMultiValueBroken() { $propPatch = new PropPatch([ '{DAV:}a' => 'foo', '{DAV:}b' => 'bar', '{DAV:}c' => null, ]); $calledA = false; $propPatch->handle(['{DAV:}a', '{DAV:}b', '{DAV:}c'], function($properties) use (&$calledA) { return 'hi'; }); $propPatch->commit(); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Property/����������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0020112�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Property/GetLastModifiedTest.php�����������������������������������0000664�0000000�0000000�00000004115�12670356754�0024470�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Property; use Sabre\DAV; use Sabre\HTTP; class GetLastModifiedTest extends \PHPUnit_Framework_TestCase { function testConstructDateTime() { $dt = new \DateTime('2010-03-14 16:35', new \DateTimeZone('UTC')); $lastMod = new GetLastModified($dt); $this->assertEquals($dt->format(\DateTime::ATOM), $lastMod->getTime()->format(\DateTime::ATOM)); } function testConstructString() { $dt = new \DateTime('2010-03-14 16:35', new \DateTimeZone('UTC')); $lastMod = new GetLastModified('2010-03-14 16:35'); $this->assertEquals($dt->format(\DateTime::ATOM), $lastMod->getTime()->format(\DateTime::ATOM)); } function testConstructInt() { $dt = new \DateTime('2010-03-14 16:35', new \DateTimeZone('UTC')); $lastMod = new GetLastModified((int)$dt->format('U')); $this->assertEquals($dt->format(\DateTime::ATOM), $lastMod->getTime()->format(\DateTime::ATOM)); } function testSerialize() { $dt = new \DateTime('2010-03-14 16:35', new \DateTimeZone('UTC')); $lastMod = new GetLastModified($dt); $doc = new \DOMDocument(); $root = $doc->createElement('d:getlastmodified'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $lastMod->serialize($server, $root); $xml = $doc->saveXML(); /* $this->assertEquals( '<?xml version="1.0"?> <d:getlastmodified xmlns:d="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">' . HTTP\Util::toHTTPDate($dt) . '</d:getlastmodified> ', $xml); */ $this->assertEquals( '<?xml version="1.0"?> <d:getlastmodified xmlns:d="DAV:">' . HTTP\Util::toHTTPDate($dt) . '</d:getlastmodified> ', $xml); $ok = false; try { GetLastModified::unserialize(DAV\XMLUtil::loadDOMDocument($xml)->firstChild, array()); } catch (DAV\Exception $e) { $ok = true; } if (!$ok) $this->markTestFailed('Unserialize should not be supported'); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Property/HrefListTest.php������������������������������������������0000664�0000000�0000000�00000004344�12670356754�0023210�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Property; use Sabre\DAV; class HrefListTest extends \PHPUnit_Framework_TestCase { function testConstruct() { $href = new HrefList(array('foo','bar')); $this->assertEquals(array('foo','bar'),$href->getHrefs()); } function testSerialize() { $href = new HrefList(array('foo','bar')); $this->assertEquals(array('foo','bar'),$href->getHrefs()); $doc = new \DOMDocument(); $root = $doc->createElement('d:anything'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $server->setBaseUri('/bla/'); $href->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:anything xmlns:d="DAV:"><d:href>/bla/foo</d:href><d:href>/bla/bar</d:href></d:anything> ', $xml); } function testSerializeNoPrefix() { $href = new HrefList(array('foo','bar'), false); $this->assertEquals(array('foo','bar'),$href->getHrefs()); $doc = new \DOMDocument(); $root = $doc->createElement('d:anything'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $server->setBaseUri('/bla/'); $href->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:anything xmlns:d="DAV:"><d:href>foo</d:href><d:href>bar</d:href></d:anything> ', $xml); } function testUnserialize() { $xml = '<?xml version="1.0"?> <d:anything xmlns:d="urn:DAV"><d:href>/bla/foo</d:href><d:href>/bla/bar</d:href></d:anything> '; $dom = new \DOMDocument(); $dom->loadXML($xml); $href = HrefList::unserialize($dom->firstChild, array()); $this->assertEquals(array('/bla/foo','/bla/bar'),$href->getHrefs()); } function testUnserializeIncompatible() { $xml = '<?xml version="1.0"?> <d:anything xmlns:d="urn:DAV"><d:href2>/bla/foo</d:href2></d:anything> '; $dom = new \DOMDocument(); $dom->loadXML($xml); $href = HrefList::unserialize($dom->firstChild, array()); $this->assertEquals(array(), $href->getHrefs()); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Property/HrefTest.php����������������������������������������������0000664�0000000�0000000�00000005362�12670356754�0022355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Property; use Sabre\DAV; class HrefTest extends \PHPUnit_Framework_TestCase { function testConstruct() { $href = new Href('path'); $this->assertEquals('path',$href->getHref()); } function testSerialize() { $href = new Href('path'); $this->assertEquals('path',$href->getHref()); $doc = new \DOMDocument(); $root = $doc->createElement('d:anything'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $server->setBaseUri('/bla/'); $href->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:anything xmlns:d="DAV:"><d:href>/bla/path</d:href></d:anything> ', $xml); } function testSerializeNoPrefix() { $href = new Href('path',false); $this->assertEquals('path',$href->getHref()); $doc = new \DOMDocument(); $root = $doc->createElement('d:anything'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $server->setBaseUri('/bla/'); $href->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:anything xmlns:d="DAV:"><d:href>path</d:href></d:anything> ', $xml); } function testUnserialize() { $xml = '<?xml version="1.0"?> <d:anything xmlns:d="urn:DAV"><d:href>/bla/path</d:href></d:anything> '; $dom = new \DOMDocument(); $dom->loadXML($xml); $href = Href::unserialize($dom->firstChild, array()); $this->assertEquals('/bla/path',$href->getHref()); } function testUnserializeIncompatible() { $xml = '<?xml version="1.0"?> <d:anything xmlns:d="urn:DAV"><d:href2>/bla/path</d:href2></d:anything> '; $dom = new \DOMDocument(); $dom->loadXML($xml); $href = Href::unserialize($dom->firstChild, array()); $this->assertNull($href); } /** * This method tests if hrefs containing & are correctly encoded. */ function testSerializeEntity() { $href = new Href('http://example.org/?a&b', false); $this->assertEquals('http://example.org/?a&b',$href->getHref()); $doc = new \DOMDocument(); $root = $doc->createElement('d:anything'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $server->setBaseUri('/bla/'); $href->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:anything xmlns:d="DAV:"><d:href>http://example.org/?a&b</d:href></d:anything> ', $xml); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Property/ResourceTypeTest.php��������������������������������������0000664�0000000�0000000�00000005232�12670356754�0024116�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Property; use Sabre\DAV; class ResourceTypeTest extends \PHPUnit_Framework_TestCase { function testConstruct() { $resourceType = new ResourceType(array('{DAV:}collection')); $this->assertEquals(array('{DAV:}collection'),$resourceType->getValue()); $resourceType = new ResourceType('{DAV:}principal'); $this->assertEquals(array('{DAV:}principal'),$resourceType->getValue()); } /** * @depends testConstruct */ function testSerialize() { $resourceType = new ResourceType(array('{DAV:}collection','{DAV:}principal')); $doc = new \DOMDocument(); $root = $doc->createElement('d:anything'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $resourceType->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:anything xmlns:d="DAV:"><d:collection/><d:principal/></d:anything> ', $xml); } /** * @depends testSerialize */ function testSerializeCustomNS() { $resourceType = new ResourceType(array('{http://example.org/NS}article')); $doc = new \DOMDocument(); $root = $doc->createElement('d:anything'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $resourceType->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:anything xmlns:d="DAV:"><custom:article xmlns:custom="http://example.org/NS"/></d:anything> ', $xml); } /** * @depends testConstruct */ function testIs() { $resourceType = new ResourceType(array('{DAV:}collection','{DAV:}principal')); $this->assertTrue($resourceType->is('{DAV:}collection')); $this->assertFalse($resourceType->is('{DAV:}blabla')); } /** * @depends testConstruct */ function testAdd() { $resourceType = new ResourceType(array('{DAV:}collection','{DAV:}principal')); $resourceType->add('{DAV:}foo'); $this->assertEquals(array('{DAV:}collection','{DAV:}principal','{DAV:}foo'), $resourceType->getValue()); } /** * @depends testConstruct */ function testUnserialize() { $xml ='<?xml version="1.0"?> <d:anything xmlns:d="DAV:"><d:collection/><d:principal/></d:anything> '; $dom = DAV\XMLUtil::loadDOMDocument($xml); $resourceType = ResourceType::unserialize($dom->firstChild, array()); $this->assertEquals(array('{DAV:}collection','{DAV:}principal'),$resourceType->getValue()); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Property/ResponseListTest.php��������������������������������������0000664�0000000�0000000�00000000622�12670356754�0024115�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Property; class ResponseListTest extends \PHPUnit_Framework_TestCase { /** * This was the only part not yet covered by other tests, so I'm going to * be lazy and (for now) only test this case. * * @expectedException InvalidArgumentException */ public function testInvalidArg() { $response = new ResponseList(array(1,2)); } } ��������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Property/ResponseTest.php������������������������������������������0000664�0000000�0000000�00000011771�12670356754�0023270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Property; use Sabre\DAV; class ResponseTest extends \PHPUnit_Framework_TestCase { function testSimple() { $innerProps = array( 200 => array( '{DAV:}displayname' => 'my file', ), 404 => array( '{DAV:}owner' => null, ) ); $property = new Response('uri',$innerProps); $this->assertEquals('uri',$property->getHref()); $this->assertEquals($innerProps,$property->getResponseProperties()); } /** * @depends testSimple */ function testSerialize() { $innerProps = array( 200 => array( '{DAV:}displayname' => 'my file', ), 404 => array( '{DAV:}owner' => null, ) ); $property = new Response('uri',$innerProps); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:root xmlns:d="DAV:">' . '<d:response>' . '<d:href>/uri</d:href>' . '<d:propstat>' . '<d:prop>' . '<d:displayname>my file</d:displayname>' . '</d:prop>' . '<d:status>HTTP/1.1 200 OK</d:status>' . '</d:propstat>' . '<d:propstat>' . '<d:prop>' . '<d:owner/>' . '</d:prop>' . '<d:status>HTTP/1.1 404 Not Found</d:status>' . '</d:propstat>' . '</d:response>' . '</d:root> ', $xml); } /** * This one is specifically for testing properties with no namespaces, which is legal xml * * @depends testSerialize */ function testSerializeEmptyNamespace() { $innerProps = array( 200 => array( '{}propertyname' => 'value', ), ); $property = new Response('uri',$innerProps); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:root xmlns:d="DAV:">' . '<d:response>' . '<d:href>/uri</d:href>' . '<d:propstat>' . '<d:prop>' . '<propertyname xmlns="">value</propertyname>' . '</d:prop>' . '<d:status>HTTP/1.1 200 OK</d:status>' . '</d:propstat>' . '</d:response>' . '</d:root> ', $xml); } /** * This one is specifically for testing properties with no namespaces, which is legal xml * * @depends testSerialize */ function testSerializeCustomNamespace() { $innerProps = array( 200 => array( '{http://sabredav.org/NS/example}propertyname' => 'value', ), ); $property = new Response('uri',$innerProps); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:root xmlns:d="DAV:">' . '<d:response>' . '<d:href>/uri</d:href>' . '<d:propstat>' . '<d:prop>' . '<x2:propertyname xmlns:x2="http://sabredav.org/NS/example">value</x2:propertyname>' . '</d:prop>' . '<d:status>HTTP/1.1 200 OK</d:status>' . '</d:propstat>' . '</d:response>' . '</d:root> ', $xml); } /** * @depends testSerialize */ function testSerializeComplexProperty() { $innerProps = array( 200 => array( '{DAV:}link' => new Href('http://sabredav.org/', false) ), ); $property = new Response('uri',$innerProps); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:root xmlns:d="DAV:">' . '<d:response>' . '<d:href>/uri</d:href>' . '<d:propstat>' . '<d:prop>' . '<d:link><d:href>http://sabredav.org/</d:href></d:link>' . '</d:prop>' . '<d:status>HTTP/1.1 200 OK</d:status>' . '</d:propstat>' . '</d:response>' . '</d:root> ', $xml); } /** * @depends testSerialize * @expectedException Sabre\DAV\Exception */ function testSerializeBreak() { $innerProps = array( 200 => array( '{DAV:}link' => new \STDClass() ), ); $property = new Response('uri',$innerProps); $doc = new \DOMDocument(); $root = $doc->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $server = new DAV\Server(); $property->serialize($server, $root); } } �������sabre-dav-2.1.10/tests/Sabre/DAV/Property/SupportedMethodSetTest.php��������������������������������0000664�0000000�0000000�00000003221�12670356754�0025263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Property; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; require_once 'Sabre/DAV/AbstractServer.php'; class SupportedMethodSetTest extends DAV\AbstractServer { public function sendPROPFIND($body) { $request = new HTTP\Request('PROPFIND', '/', ['Depth' => '0' ]); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); } /** */ function testMethods() { $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:supported-method-set /> </d:prop> </d:propfind>'; $this->sendPROPFIND($xml); $this->assertEquals(207, $this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); $this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-method-set'); $this->assertEquals(1,count($data),'We expected 1 \'d:supported-method-set\' element'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); $this->assertEquals(1,count($data),'We expected 1 \'d:status\' element'); $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0],'The status for this property should have been 200'); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Property/SupportedReportSetTest.php��������������������������������0000664�0000000�0000000�00000010660�12670356754�0025323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Property; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; require_once 'Sabre/DAV/AbstractServer.php'; class SupportedReportSetTest extends DAV\AbstractServer { public function sendPROPFIND($body) { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'PROPFIND', 'HTTP_DEPTH' => '0', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($body); $this->server->httpRequest = ($request); $this->server->exec(); } /** */ function testNoReports() { $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:supported-report-set /> </d:prop> </d:propfind>'; $this->sendPROPFIND($xml); $this->assertEquals(207, $this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); $this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set'); $this->assertEquals(1,count($data),'We expected 1 \'d:supported-report-set\' element'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); $this->assertEquals(1,count($data),'We expected 1 \'d:status\' element'); $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0],'The status for this property should have been 200'); } /** * @depends testNoReports */ function testCustomReport() { // Intercepting the report property $this->server->on('propFind', function(DAV\PropFind $propFind, DAV\INode $node) { if ($prop = $propFind->get('{DAV:}supported-report-set')) { $prop->addReport('{http://www.rooftopsolutions.nl/testnamespace}myreport'); $prop->addReport('{DAV:}anotherreport'); } },200); $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:supported-report-set /> </d:prop> </d:propfind>'; $this->sendPROPFIND($xml); $this->assertEquals(207, $this->response->status,'We expected a multi-status response. Full response body: ' . $this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('x','http://www.rooftopsolutions.nl/testnamespace'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); $this->assertEquals(1,count($data),'We expected 1 \'d:prop\' element'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set'); $this->assertEquals(1,count($data),'We expected 1 \'d:supported-report-set\' element'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report'); $this->assertEquals(2,count($data),'We expected 2 \'d:supported-report\' elements'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report'); $this->assertEquals(2,count($data),'We expected 2 \'d:report\' elements'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report/x:myreport'); $this->assertEquals(1,count($data),'We expected 1 \'x:myreport\' element. Full body: ' . $this->response->body); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supported-report-set/d:supported-report/d:report/d:anotherreport'); $this->assertEquals(1,count($data),'We expected 1 \'d:anotherreport\' element. Full body: ' . $this->response->body); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); $this->assertEquals(1,count($data),'We expected 1 \'d:status\' element'); $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0],'The status for this property should have been 200'); } } ��������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropertyStorage/���������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0021437�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropertyStorage/Backend/�������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0022766�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropertyStorage/Backend/AbstractPDOTest.php������������������������0000664�0000000�0000000�00000007640�12670356754�0026454�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\PropertyStorage\Backend; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase { /** * Should return an instance of \PDO with the current tables initialized, * and some test records. */ abstract function getPDO(); function getBackend() { return new PDO($this->getPDO()); } function testPropFind() { $backend = $this->getBackend(); $propFind = new PropFind('dir', ['{DAV:}displayname']); $backend->propFind('dir', $propFind); $this->assertEquals('Directory', $propFind->get('{DAV:}displayname')); } function testPropFindNothingToDo() { $backend = $this->getBackend(); $propFind = new PropFind('dir', ['{DAV:}displayname']); $propFind->set('{DAV:}displayname', 'foo'); $backend->propFind('dir', $propFind); $this->assertEquals('foo', $propFind->get('{DAV:}displayname')); } /** * @depends testPropFind */ function testPropPatchUpdate() { $backend = $this->getBackend(); $propPatch = new PropPatch(['{DAV:}displayname' => 'bar']); $backend->propPatch('dir', $propPatch); $propPatch->commit(); $propFind = new PropFind('dir', ['{DAV:}displayname']); $backend->propFind('dir', $propFind); $this->assertEquals('bar', $propFind->get('{DAV:}displayname')); } /** * @depends testPropFind */ function testPropPatchRemove() { $backend = $this->getBackend(); $propPatch = new PropPatch(['{DAV:}displayname' => null]); $backend->propPatch('dir', $propPatch); $propPatch->commit(); $propFind = new PropFind('dir', ['{DAV:}displayname']); $backend->propFind('dir', $propFind); $this->assertEquals(null, $propFind->get('{DAV:}displayname')); } /** * @depends testPropFind */ function testDelete() { $backend = $this->getBackend(); $backend->delete('dir'); $propFind = new PropFind('dir', ['{DAV:}displayname']); $backend->propFind('dir', $propFind); $this->assertEquals(null, $propFind->get('{DAV:}displayname')); } /** * @depends testPropFind */ function testMove() { $backend = $this->getBackend(); // Creating a new child property. $propPatch = new PropPatch(['{DAV:}displayname' => 'child']); $backend->propPatch('dir/child', $propPatch); $propPatch->commit(); $backend->move('dir','dir2'); // Old 'dir' $propFind = new PropFind('dir', ['{DAV:}displayname']); $backend->propFind('dir', $propFind); $this->assertEquals(null, $propFind->get('{DAV:}displayname')); // Old 'dir/child' $propFind = new PropFind('dir/child', ['{DAV:}displayname']); $backend->propFind('dir/child', $propFind); $this->assertEquals(null, $propFind->get('{DAV:}displayname')); // New 'dir2' $propFind = new PropFind('dir2', ['{DAV:}displayname']); $backend->propFind('dir2', $propFind); $this->assertEquals('Directory', $propFind->get('{DAV:}displayname')); // New 'dir2/child' $propFind = new PropFind('dir2/child', ['{DAV:}displayname']); $backend->propFind('dir2/child', $propFind); $this->assertEquals('child', $propFind->get('{DAV:}displayname')); } /** * @depends testPropFind */ function testDeepDelete() { $backend = $this->getBackend(); $propPatch = new PropPatch(['{DAV:}displayname' => 'child']); $backend->propPatch('dir/child', $propPatch); $propPatch->commit(); $backend->delete('dir'); $propFind = new PropFind('dir/child', ['{DAV:}displayname']); $backend->propFind('dir/child', $propFind); $this->assertEquals(null, $propFind->get('{DAV:}displayname')); } } ������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropertyStorage/Backend/Mock.php�����������������������������������0000664�0000000�0000000�00000005572�12670356754�0024401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\PropertyStorage\Backend; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; class Mock implements BackendInterface { public $data = []; /** * Fetches properties for a path. * * This method received a PropFind object, which contains all the * information about the properties that need to be fetched. * * Ususually you would just want to call 'get404Properties' on this object, * as this will give you the _exact_ list of properties that need to be * fetched, and haven't yet. * * @param string $path * @param PropFind $propFind * @return void */ public function propFind($path, PropFind $propFind) { if (!isset($this->data[$path])) { return; } foreach($this->data[$path] as $name=>$value) { $propFind->set($name, $value); } } /** * Updates properties for a path * * This method received a PropPatch object, which contains all the * information about the update. * * Usually you would want to call 'handleRemaining' on this object, to get; * a list of all properties that need to be stored. * * @param string $path * @param PropPatch $propPatch * @return void */ public function propPatch($path, PropPatch $propPatch) { if (!isset($this->data[$path])) { $this->data[$path] = []; } $propPatch->handleRemaining(function($properties) use ($path) { foreach($properties as $propName=>$propValue) { if (is_null($propValue)) { unset($this->data[$path][$propName]); } else { $this->data[$path][$propName] = $propValue; } return true; } }); } /** * This method is called after a node is deleted. * * This allows a backend to clean up all associated properties. */ public function delete($path) { unset($this->data[$path]); } /** * This method is called after a successful MOVE * * This should be used to migrate all properties from one path to another. * Note that entire collections may be moved, so ensure that all properties * for children are also moved along. * * @param string $source * @param string $destination * @return void */ public function move($source, $destination) { foreach($this->data as $path => $props) { if ($path === $source) { $this->data[$destination] = $props; unset($this->data[$path]); continue; } if (strpos($path, $source . '/')===0) { $this->data[$destination . substr($path, strlen($source)+1)] = $props; unset($this->data[$path]); } } } } ��������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropertyStorage/Backend/PDOMysqlTest.php���������������������������0000664�0000000�0000000�00000001377�12670356754�0026017�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\PropertyStorage\Backend; class PDOMySqlTest extends AbstractPDOTest { function getPDO() { $pdo = \Sabre\TestUtil::getMySQLDB(); if (!$pdo) $this->markTestSkipped('MySQL is not enabled'); $setupSql = file_get_contents(__DIR__ . '/../../../../../examples/sql/mysql.propertystorage.sql'); // Sloppy multi-query, but it works $setupSql = explode(';', $setupSql); $pdo->exec('DROP TABLE IF EXISTS propertystorage'); foreach($setupSql as $sql) { if (!trim($sql)) continue; $pdo->exec($sql); } $pdo->exec('INSERT INTO propertystorage (path, name, value) VALUES ("dir", "{DAV:}displayname", "Directory")'); return $pdo; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropertyStorage/Backend/PDOSqliteTest.php��������������������������0000664�0000000�0000000�00000001517�12670356754�0026147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\PropertyStorage\Backend; class PDOSqliteTest extends AbstractPDOTest { function getPDO() { $pdo = \Sabre\TestUtil::getSqliteDB(); if (!$pdo) $this->markTestSkipped('Sqlite is not enabled'); $setupSql = file_get_contents(__DIR__ . '/../../../../../examples/sql/sqlite.propertystorage.sql'); // Sloppy multi-query, but it works $setupSql = explode(';', $setupSql); $pdo->exec('DROP TABLE IF EXISTS propertystorage'); foreach($setupSql as $sql) { if (!trim($sql)) continue; $pdo->exec($sql); } $pdo->exec('INSERT INTO propertystorage (path, name, value) VALUES ("dir", "{DAV:}displayname", "Directory")'); return $pdo; } function tearDown() { \Sabre\TestUtil::clearTempDir(); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/PropertyStorage/PluginTest.php�������������������������������������0000664�0000000�0000000�00000004757�12670356754�0024263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\PropertyStorage; class PluginTest extends \Sabre\DAVServerTest { protected $backend; protected $plugin; protected $setupFiles = true; function setUp() { parent::setUp(); $this->backend = new Backend\Mock(); $this->plugin = new Plugin( $this->backend ); $this->server->addPlugin($this->plugin); } function testSetProperty() { $this->server->updateProperties('', ['{DAV:}displayname' => 'hi']); $this->assertEquals([ '' => [ '{DAV:}displayname' => 'hi', ] ], $this->backend->data); } /** * @depends testSetProperty */ function testGetProperty() { $this->testSetProperty(); $result = $this->server->getProperties('', ['{DAV:}displayname']); $this->assertEquals([ '{DAV:}displayname' => 'hi', ], $result); } /** * @depends testSetProperty */ function testDeleteProperty() { $this->testSetProperty(); $this->server->emit('afterUnbind', ['']); $this->assertEquals([],$this->backend->data); } function testMove() { $this->server->tree->getNodeForPath('files')->createFile('source'); $this->server->updateProperties('files/source', ['{DAV:}displayname' => 'hi']); $request = new \Sabre\HTTP\Request('MOVE', '/files/source', ['Destination' => '/files/dest']); $this->assertHTTPStatus(201, $request); $result = $this->server->getProperties('/files/dest', ['{DAV:}displayname']); $this->assertEquals([ '{DAV:}displayname' => 'hi', ], $result); $this->server->tree->getNodeForPath('files')->createFile('source'); $result = $this->server->getProperties('/files/source', ['{DAV:}displayname']); $this->assertEquals([], $result); } /** * @depends testDeleteProperty */ function testSetPropertyInFilteredPath() { $this->plugin->pathFilter = function($path) { return false; }; $this->server->updateProperties('', ['{DAV:}displayname' => 'hi']); $this->assertEquals([], $this->backend->data); } /** * @depends testSetPropertyInFilteredPath */ function testGetPropertyInFilteredPath() { $this->testSetPropertyInFilteredPath(); $result = $this->server->getProperties('', ['{DAV:}displayname']); $this->assertEquals([], $result); } } �����������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerCopyMoveTest.php���������������������������������������������0000664�0000000�0000000�00000014121�12670356754�0022566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; class ServerCopyMoveTest extends \PHPUnit_Framework_TestCase { private $response; /** * @var Server */ private $server; function setUp() { $this->response = new HTTP\ResponseMock(); $dir = new FS\Directory(SABRE_TEMPDIR); $tree = new Tree($dir); $this->server = new Server($tree); $this->server->sapi = new HTTP\SapiMock(); $this->server->debugExceptions = true; $this->server->httpResponse = $this->response; file_put_contents(SABRE_TEMPDIR . '/test.txt', 'Test contents'); file_put_contents(SABRE_TEMPDIR . '/test2.txt', 'Test contents2'); mkdir(SABRE_TEMPDIR . '/col'); file_put_contents(SABRE_TEMPDIR . 'col/test.txt', 'Test contents'); } function tearDown() { $cleanUp = array('test.txt','testput.txt','testcol','test2.txt','test3.txt','col/test.txt','col','col2/test.txt','col2'); foreach($cleanUp as $file) { $tmpFile = SABRE_TEMPDIR . '/' . $file; if (file_exists($tmpFile)) { if (is_dir($tmpFile)) { rmdir($tmpFile); } else { unlink($tmpFile); } } } } function testCopyOverWrite() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/test2.txt', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(204, $this->response->status, 'Received an incorrect HTTP status. Full body inspection: ' . $this->response->body); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ), $this->response->getHeaders() ); $this->assertEquals('Test contents', file_get_contents(SABRE_TEMPDIR. '/test2.txt')); } function testCopyToSelf() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/test.txt', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(403, $this->response->status, 'Received an incorrect HTTP status. Full body inspection: ' . $this->response->body); $this->assertEquals('Test contents', file_get_contents(SABRE_TEMPDIR. '/test.txt')); } function testNonExistantParent() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/testcol2/test2.txt', 'HTTP_OVERWRITE' => 'F', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $this->response->getHeaders() ); $this->assertEquals(409, $this->response->status); } function testRandomOverwriteHeader() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/testcol2/test2.txt', 'HTTP_OVERWRITE' => 'SURE!', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(400, $this->response->status); } function testCopyDirectory() { $serverVars = array( 'REQUEST_URI' => '/col', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/col2', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(201, $this->response->status, 'Full response: ' . $this->response->getBody(true)); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ), $this->response->getHeaders() ); $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/col2/test.txt')); } function testSimpleCopyFile() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/test3.txt', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ), $this->response->getHeaders() ); $this->assertEquals(201, $this->response->status); $this->assertEquals('Test contents', file_get_contents(SABRE_TEMPDIR . '/test3.txt')); } function testSimpleCopyCollection() { $serverVars = array( 'REQUEST_URI' => '/col', 'REQUEST_METHOD' => 'COPY', 'HTTP_DESTINATION' => '/col2', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(201, $this->response->status, 'Incorrect status received. Full response body: ' . $this->response->body); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ), $this->response->getHeaders() ); $this->assertEquals('Test contents',file_get_contents(SABRE_TEMPDIR . '/col2/test.txt')); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerEventsTest.php�����������������������������������������������0000664�0000000�0000000�00000004140�12670356754�0022271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAV/AbstractServer.php'; class ServerEventsTest extends AbstractServer { private $tempPath; private $exception; function testAfterBind() { $this->server->on('afterBind', [$this,'afterBindHandler']); $newPath = 'afterBind'; $this->tempPath = ''; $this->server->createFile($newPath,'body'); $this->assertEquals($newPath, $this->tempPath); } function afterBindHandler($path) { $this->tempPath = $path; } function testAfterResponse() { $mock = $this->getMock('stdClass', array('afterResponseCallback')); $mock->expects($this->once())->method('afterResponseCallback'); $this->server->on('afterResponse', [$mock, 'afterResponseCallback']); $this->server->httpRequest = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'GET', 'REQUEST_URI' => '/test.txt', )); $this->server->exec(); } function testBeforeBindCancel() { $this->server->on('beforeBind', [$this,'beforeBindCancelHandler']); $this->assertFalse($this->server->createFile('bla','body')); // Also testing put() $req = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'PUT', 'REQUEST_URI' => '/barbar', )); $this->server->httpRequest = $req; $this->server->exec(); $this->assertEquals('',$this->server->httpResponse->status); } function beforeBindCancelHandler() { return false; } function testException() { $this->server->on('exception', [$this, 'exceptionHandler']); $req = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'GET', 'REQUEST_URI' => '/not/exisitng', )); $this->server->httpRequest = $req; $this->server->exec(); $this->assertInstanceOf('Sabre\\DAV\\Exception\\NotFound', $this->exception); } function exceptionHandler(Exception $exception) { $this->exception = $exception; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerMKCOLTest.php������������������������������������������������0000664�0000000�0000000�00000025277�12670356754�0021710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; class ServerMKCOLTest extends AbstractServer { function testMkcol() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody(""); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ),$this->response->getHeaders()); $this->assertEquals(201, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertTrue(is_dir($this->tempDir . '/testcol')); } /** * @depends testMkcol */ function testMKCOLUnknownBody() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody("Hello"); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(415, $this->response->status); } /** * @depends testMkcol */ function testMKCOLBrokenXML() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', 'HTTP_CONTENT_TYPE' => 'application/xml', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody("Hello"); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(400, $this->response->status); } /** * @depends testMkcol */ function testMKCOLUnknownXML() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', 'HTTP_CONTENT_TYPE' => 'application/xml', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?><html></html>'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(415, $this->response->status); } /** * @depends testMkcol */ function testMKCOLNoResourceType() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', 'HTTP_CONTENT_TYPE' => 'application/xml', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <mkcol xmlns="DAV:"> <set> <prop> <displayname>Evert</displayname> </prop> </set> </mkcol>'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(400, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); } /** * @depends testMKCOLNoResourceType */ function testMKCOLIncorrectResourceType() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', 'HTTP_CONTENT_TYPE' => 'application/xml', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <mkcol xmlns="DAV:"> <set> <prop> <resourcetype><blabla /></resourcetype> </prop> </set> </mkcol>'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(403, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); } /** * @depends testMKCOLIncorrectResourceType */ function testMKCOLIncorrectResourceType2() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', 'HTTP_CONTENT_TYPE' => 'application/xml', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <mkcol xmlns="DAV:"> <set> <prop> <resourcetype><collection /><blabla /></resourcetype> </prop> </set> </mkcol>'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(403, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); } /** * @depends testMKCOLIncorrectResourceType2 */ function testMKCOLSuccess() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', 'HTTP_CONTENT_TYPE' => 'application/xml', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <mkcol xmlns="DAV:"> <set> <prop> <resourcetype><collection /></resourcetype> </prop> </set> </mkcol>'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ),$this->response->getHeaders()); $this->assertEquals(201, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); } /** * @depends testMKCOLIncorrectResourceType2 */ function testMKCOLWhiteSpaceResourceType() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', 'HTTP_CONTENT_TYPE' => 'application/xml', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <mkcol xmlns="DAV:"> <set> <prop> <resourcetype> <collection /> </resourcetype> </prop> </set> </mkcol>'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Length' => ['0'], ),$this->response->getHeaders()); $this->assertEquals(201, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); } /** * @depends testMKCOLIncorrectResourceType2 */ function testMKCOLNoParent() { $serverVars = array( 'REQUEST_URI' => '/testnoparent/409me', 'REQUEST_METHOD' => 'MKCOL', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody(''); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(409, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); } /** * @depends testMKCOLIncorrectResourceType2 */ function testMKCOLParentIsNoCollection() { $serverVars = array( 'REQUEST_URI' => '/test.txt/409me', 'REQUEST_METHOD' => 'MKCOL', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody(''); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(409, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); } /** * @depends testMKCOLIncorrectResourceType2 */ function testMKCOLAlreadyExists() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'MKCOL', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody(''); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'], ),$this->response->getHeaders()); $this->assertEquals(405, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); } /** * @depends testMKCOLSuccess * @depends testMKCOLAlreadyExists */ function testMKCOLAndProps() { $serverVars = array( 'REQUEST_URI' => '/testcol', 'REQUEST_METHOD' => 'MKCOL', 'HTTP_CONTENT_TYPE' => 'application/xml', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <mkcol xmlns="DAV:"> <set> <prop> <resourcetype><collection /></resourcetype> <displayname>my new collection</displayname> </prop> </set> </mkcol>'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(207, $this->response->status, 'Wrong statuscode received. Full response body: ' .$this->response->body); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerPluginTest.php�����������������������������������������������0000664�0000000�0000000�00000004504�12670356754�0022267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAV/AbstractServer.php'; require_once 'Sabre/DAV/TestPlugin.php'; class ServerPluginTest extends AbstractServer { /** * @var Sabre\DAV\TestPlugin */ protected $testPlugin; function setUp() { parent::setUp(); $testPlugin = new TestPlugin(); $this->server->addPlugin($testPlugin); $this->testPlugin = $testPlugin; } /** */ function testBaseClass() { $p = new ServerPluginMock(); $this->assertEquals(array(),$p->getFeatures()); $this->assertEquals(array(),$p->getHTTPMethods('')); } function testOptions() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'OPTIONS', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'DAV' => ['1, 3, extended-mkcol, drinking'], 'MS-Author-Via' => ['DAV'], 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT, BEER, WINE'], 'Accept-Ranges' => ['bytes'], 'Content-Length' => ['0'], 'X-Sabre-Version' => [Version::VERSION], ),$this->response->getHeaders()); $this->assertEquals(200, $this->response->status); $this->assertEquals('', $this->response->body); $this->assertEquals('OPTIONS',$this->testPlugin->beforeMethod); } function testGetPlugin() { $this->assertEquals($this->testPlugin,$this->server->getPlugin(get_class($this->testPlugin))); } function testUnknownPlugin() { $this->assertNull($this->server->getPlugin('SomeRandomClassName')); } function testGetSupportedReportSet() { $this->assertEquals(array(), $this->testPlugin->getSupportedReportSet('/')); } function testGetPlugins() { $this->assertEquals( array( get_class($this->testPlugin) => $this->testPlugin, 'core' => $this->server->getPlugin('core'), ), $this->server->getPlugins() ); } } class ServerPluginMock extends ServerPlugin { function initialize(Server $s) { } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerPreconditionTest.php�����������������������������������������0000664�0000000�0000000�00000025767�12670356754�0023504�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; class ServerPreconditionsTest extends \PHPUnit_Framework_TestCase { /** * @expectedException Sabre\DAV\Exception\PreconditionFailed */ function testIfMatchNoNode() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('GET', '/bar', ['If-Match' => '*']); $httpResponse = new HTTP\Response(); $server->checkPreconditions($httpRequest, $httpResponse); } /** */ function testIfMatchHasNode() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '*']); $httpResponse = new HTTP\Response(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** * @expectedException Sabre\DAV\Exception\PreconditionFailed */ function testIfMatchWrongEtag() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '1234']); $httpResponse = new HTTP\Response(); $server->checkPreconditions($httpRequest, $httpResponse); } /** */ function testIfMatchCorrectEtag() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '"abc123"']); $httpResponse = new HTTP\Response(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** * Evolution sometimes uses \" instead of " for If-Match headers. * * @depends testIfMatchCorrectEtag */ function testIfMatchEvolutionEtag() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '\\"abc123\\"']); $httpResponse = new HTTP\Response(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** */ function testIfMatchMultiple() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('GET', '/foo', ['If-Match' => '"hellothere", "abc123"']); $httpResponse = new HTTP\Response(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** */ function testIfNoneMatchNoNode() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('GET', '/bar', ['If-None-Match' => '*']); $httpResponse = new HTTP\Response(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** * @expectedException Sabre\DAV\Exception\PreconditionFailed */ function testIfNoneMatchHasNode() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '*']); $httpResponse = new HTTP\Response(); $server->checkPreconditions($httpRequest, $httpResponse); } /** */ function testIfNoneMatchWrongEtag() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234"']); $httpResponse = new HTTP\Response(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** */ function testIfNoneMatchWrongEtagMultiple() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234", "5678"']); $httpResponse = new HTTP\Response(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** * @expectedException Sabre\DAV\Exception\PreconditionFailed */ public function testIfNoneMatchCorrectEtag() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"abc123"']); $httpResponse = new HTTP\Response(); $server->checkPreconditions($httpRequest, $httpResponse); } /** * @expectedException Sabre\DAV\Exception\PreconditionFailed */ public function testIfNoneMatchCorrectEtagMultiple() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('POST', '/foo', ['If-None-Match' => '"1234, "abc123"']); $httpResponse = new HTTP\Response(); $server->checkPreconditions($httpRequest, $httpResponse); } /** */ public function testIfNoneMatchCorrectEtagAsGet() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = new HTTP\Request('GET', '/foo', ['If-None-Match' => '"abc123"']); $server->httpResponse = new HTTP\ResponseMock(); $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse)); $this->assertEquals(304, $server->httpResponse->getStatus()); $this->assertEquals(['ETag' => ['"abc123"']], $server->httpResponse->getHeaders()); } /** * This was a test written for issue #515. */ public function testNoneMatchCorrectEtagEnsureSapiSent() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $server->sapi = new HTTP\SapiMock(); HTTP\SapiMock::$sent = 0; $httpRequest = new HTTP\Request('GET', '/foo', ['If-None-Match' => '"abc123"']); $server->httpRequest = $httpRequest; $server->httpResponse = new HTTP\ResponseMock(); $server->exec(); $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse)); $this->assertEquals(304, $server->httpResponse->getStatus()); $this->assertEquals([ 'ETag' => ['"abc123"'], 'X-Sabre-Version' => [Version::VERSION], ], $server->httpResponse->getHeaders()); $this->assertEquals(1, HTTP\SapiMock::$sent); } /** */ public function testIfModifiedSinceUnModified() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 GMT', 'REQUEST_URI' => '/foo' )); $server->httpResponse = new HTTP\ResponseMock(); $this->assertFalse($server->checkPreconditions($httpRequest, $server->httpResponse)); $this->assertEquals(304, $server->httpResponse->status); $this->assertEquals(array( 'Last-Modified' => ['Sat, 06 Apr 1985 23:30:00 GMT'], ), $server->httpResponse->getHeaders()); } /** */ public function testIfModifiedSinceModified() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_IF_MODIFIED_SINCE' => 'Tue, 06 Nov 1984 08:49:37 GMT', 'REQUEST_URI' => '/foo' )); $httpRequest = $httpRequest; $httpResponse = new HTTP\ResponseMock(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** */ public function testIfModifiedSinceInvalidDate() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_IF_MODIFIED_SINCE' => 'Your mother', 'REQUEST_URI' => '/foo' )); $httpRequest = $httpRequest; $httpResponse = new HTTP\ResponseMock(); // Invalid dates must be ignored, so this should return true $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** */ public function testIfModifiedSinceInvalidDate2() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 EST', 'REQUEST_URI' => '/foo' )); $httpResponse = new HTTP\ResponseMock(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** */ public function testIfUnmodifiedSinceUnModified() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 06 Nov 1994 08:49:37 GMT', 'REQUEST_URI' => '/foo' )); $httpResponse = new HTTP\Response(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } /** * @expectedException Sabre\DAV\Exception\PreconditionFailed */ public function testIfUnmodifiedSinceModified() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_IF_UNMODIFIED_SINCE' => 'Tue, 06 Nov 1984 08:49:37 GMT', 'REQUEST_URI' => '/foo' )); $httpResponse = new HTTP\ResponseMock(); $server->checkPreconditions($httpRequest, $httpResponse); } /** */ public function testIfUnmodifiedSinceInvalidDate() { $root = new SimpleCollection('root',array(new ServerPreconditionsNode())); $server = new Server($root); $httpRequest = HTTP\Sapi::createFromServerArray(array( 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 06 Nov 1984 08:49:37 CET', 'REQUEST_URI' => '/foo' )); $httpResponse = new HTTP\ResponseMock(); $this->assertTrue($server->checkPreconditions($httpRequest, $httpResponse)); } } class ServerPreconditionsNode extends File { function getETag() { return '"abc123"'; } function getLastModified() { /* my birthday & time, I believe */ return strtotime('1985-04-07 01:30 +02:00'); } function getName() { return 'foo'; } } ���������sabre-dav-2.1.10/tests/Sabre/DAV/ServerPropsInfiniteDepthTest.php�����������������������������������0000664�0000000�0000000�00000015021�12670356754�0024603�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAV/AbstractServer.php'; class ServerPropsInfiniteDepthTest extends AbstractServer { protected function getRootNode() { return new FSExt\Directory(SABRE_TEMPDIR); } function setUp() { if (file_exists(SABRE_TEMPDIR.'../.sabredav')) unlink(SABRE_TEMPDIR.'../.sabredav'); parent::setUp(); file_put_contents(SABRE_TEMPDIR . '/test2.txt', 'Test contents2'); mkdir(SABRE_TEMPDIR . '/col'); mkdir(SABRE_TEMPDIR . '/col/col'); file_put_contents(SABRE_TEMPDIR . 'col/col/test.txt', 'Test contents'); $this->server->addPlugin(new Locks\Plugin(new Locks\Backend\File(SABRE_TEMPDIR . '/.locksdb'))); $this->server->enablePropfindDepthInfinity = true; } function tearDown() { parent::tearDown(); if (file_exists(SABRE_TEMPDIR.'../.locksdb')) unlink(SABRE_TEMPDIR.'../.locksdb'); } private function sendRequest($body) { $request = new HTTP\Request('PROPFIND', '/', ['Depth' => 'infinity']); $request->setBody($body); $this->server->httpRequest = $request; $this->server->exec(); } public function testPropFindEmptyBody() { $hasFired = false; $self = $this; $this->sendRequest(""); $this->assertEquals(207, $this->response->status, 'Incorrect status received. Full response body: ' . $this->response->getBodyAsString()); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'DAV' => ['1, 3, extended-mkcol, 2'], 'Vary' => ['Brief,Prefer'], ), $this->response->getHeaders() ); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); $this->assertEquals('/',(string)$data,'href element should have been /'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype'); // 8 resources are to be returned: /, col, col/col, col/col/test.txt, dir, dir/child.txt, test.txt and test2.txt $this->assertEquals(8,count($data)); } function testSupportedLocks() { $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:supportedlock /> </d:prop> </d:propfind>'; $this->sendRequest($xml); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry'); $this->assertEquals(16,count($data),'We expected sixteen \'d:lockentry\' tags'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope'); $this->assertEquals(16,count($data),'We expected sixteen \'d:lockscope\' tags'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype'); $this->assertEquals(16,count($data),'We expected sixteen \'d:locktype\' tags'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:shared'); $this->assertEquals(8,count($data),'We expected eight \'d:shared\' tags'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:exclusive'); $this->assertEquals(8,count($data),'We expected eight \'d:exclusive\' tags'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype/d:write'); $this->assertEquals(16,count($data),'We expected sixteen \'d:write\' tags'); } function testLockDiscovery() { $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:lockdiscovery /> </d:prop> </d:propfind>'; $this->sendRequest($xml); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:lockdiscovery'); $this->assertEquals(8,count($data),'We expected eight \'d:lockdiscovery\' tags'); } function testUnknownProperty() { $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:macaroni /> </d:prop> </d:propfind>'; $this->sendRequest($xml); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $pathTests = array( '/d:multistatus', '/d:multistatus/d:response', '/d:multistatus/d:response/d:propstat', '/d:multistatus/d:response/d:propstat/d:status', '/d:multistatus/d:response/d:propstat/d:prop', '/d:multistatus/d:response/d:propstat/d:prop/d:macaroni', ); foreach($pathTests as $test) { $this->assertTrue(count($xml->xpath($test))==true,'We expected the ' . $test . ' element to appear in the response, we got: ' . $body); } $val = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); $this->assertEquals(8,count($val),$body); $this->assertEquals('HTTP/1.1 404 Not Found',(string)$val[0]); } /** */ public function testParsePropPatchRequest() { $body = '<?xml version="1.0"?> <d:propertyupdate xmlns:d="DAV:" xmlns:s="http://sabredav.org/NS/test"> <d:set><d:prop><s:someprop>somevalue</s:someprop></d:prop></d:set> <d:remove><d:prop><s:someprop2 /></d:prop></d:remove> <d:set><d:prop><s:someprop3>removeme</s:someprop3></d:prop></d:set> <d:remove><d:prop><s:someprop3 /></d:prop></d:remove> </d:propertyupdate>'; $result = $this->server->parsePropPatchRequest($body); $this->assertEquals(array( '{http://sabredav.org/NS/test}someprop' => 'somevalue', '{http://sabredav.org/NS/test}someprop2' => null, '{http://sabredav.org/NS/test}someprop3' => null, ), $result); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerPropsTest.php������������������������������������������������0000664�0000000�0000000�00000030665�12670356754�0022143�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; require_once 'Sabre/DAV/AbstractServer.php'; class ServerPropsTest extends AbstractServer { protected function getRootNode() { return new FSExt\Directory(SABRE_TEMPDIR); } function setUp() { if (file_exists(SABRE_TEMPDIR.'../.sabredav')) unlink(SABRE_TEMPDIR.'../.sabredav'); parent::setUp(); file_put_contents(SABRE_TEMPDIR . '/test2.txt', 'Test contents2'); mkdir(SABRE_TEMPDIR . '/col'); file_put_contents(SABRE_TEMPDIR . 'col/test.txt', 'Test contents'); $this->server->addPlugin(new Locks\Plugin(new Locks\Backend\File(SABRE_TEMPDIR . '/.locksdb'))); } function tearDown() { parent::tearDown(); if (file_exists(SABRE_TEMPDIR.'../.locksdb')) unlink(SABRE_TEMPDIR.'../.locksdb'); } private function sendRequest($body, $path = '/', $headers = ['Depth' => '0']) { $request = new HTTP\Request('PROPFIND', $path, $headers, $body); $this->server->httpRequest = $request; $this->server->exec(); } public function testPropFindEmptyBody() { $this->sendRequest(""); $this->assertEquals(207, $this->response->status); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'DAV' => ['1, 3, extended-mkcol, 2'], 'Vary' => ['Brief,Prefer'], ), $this->response->getHeaders() ); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); $this->assertEquals('/',(string)$data,'href element should have been /'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype'); $this->assertEquals(1,count($data)); } public function testPropFindEmptyBodyFile() { $this->sendRequest("", '/test2.txt', []); $this->assertEquals(207, $this->response->status); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'DAV' => ['1, 3, extended-mkcol, 2'], 'Vary' => ['Brief,Prefer'], ), $this->response->getHeaders() ); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); $this->assertEquals('/test2.txt',(string)$data,'href element should have been /'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength'); $this->assertEquals(1,count($data)); } function testSupportedLocks() { $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:supportedlock /> </d:prop> </d:propfind>'; $this->sendRequest($xml); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry'); $this->assertEquals(2,count($data),'We expected two \'d:lockentry\' tags'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope'); $this->assertEquals(2,count($data),'We expected two \'d:lockscope\' tags'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype'); $this->assertEquals(2,count($data),'We expected two \'d:locktype\' tags'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:shared'); $this->assertEquals(1,count($data),'We expected a \'d:shared\' tag'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:lockscope/d:exclusive'); $this->assertEquals(1,count($data),'We expected a \'d:exclusive\' tag'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:supportedlock/d:lockentry/d:locktype/d:write'); $this->assertEquals(2,count($data),'We expected two \'d:write\' tags'); } function testLockDiscovery() { $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:lockdiscovery /> </d:prop> </d:propfind>'; $this->sendRequest($xml); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:lockdiscovery'); $this->assertEquals(1,count($data),'We expected a \'d:lockdiscovery\' tag'); } function testUnknownProperty() { $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:"> <d:prop> <d:macaroni /> </d:prop> </d:propfind>'; $this->sendRequest($xml); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $pathTests = array( '/d:multistatus', '/d:multistatus/d:response', '/d:multistatus/d:response/d:propstat', '/d:multistatus/d:response/d:propstat/d:status', '/d:multistatus/d:response/d:propstat/d:prop', '/d:multistatus/d:response/d:propstat/d:prop/d:macaroni', ); foreach($pathTests as $test) { $this->assertTrue(count($xml->xpath($test))==true,'We expected the ' . $test . ' element to appear in the response, we got: ' . $body); } $val = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); $this->assertEquals(1,count($val),$body); $this->assertEquals('HTTP/1.1 404 Not Found',(string)$val[0]); } public function testParsePropPatchRequest() { $body = '<?xml version="1.0"?> <d:propertyupdate xmlns:d="DAV:" xmlns:s="http://sabredav.org/NS/test"> <d:set><d:prop><s:someprop>somevalue</s:someprop></d:prop></d:set> <d:remove><d:prop><s:someprop2 /></d:prop></d:remove> <d:set><d:prop><s:someprop3>removeme</s:someprop3></d:prop></d:set> <d:remove><d:prop><s:someprop3 /></d:prop></d:remove> </d:propertyupdate>'; $result = $this->server->parsePropPatchRequest($body); $this->assertEquals(array( '{http://sabredav.org/NS/test}someprop' => 'somevalue', '{http://sabredav.org/NS/test}someprop2' => null, '{http://sabredav.org/NS/test}someprop3' => null, ), $result); } public function testUpdateProperties() { $props = array( '{http://sabredav.org/NS/test}someprop' => 'somevalue', ); $result = $this->server->updateProperties('/test2.txt',$props); $this->assertEquals(array( '{http://sabredav.org/NS/test}someprop' => 200 ), $result); } public function testUpdatePropertiesProtected() { $props = array( '{http://sabredav.org/NS/test}someprop' => 'somevalue', '{DAV:}getcontentlength' => 50, ); $result = $this->server->updateProperties('/test2.txt',$props); $this->assertEquals(array( '{http://sabredav.org/NS/test}someprop' => 424, '{DAV:}getcontentlength' => 403, ), $result); } public function testUpdatePropertiesFail1() { $dir = new Mock\PropertiesCollection('root', []); $dir->failMode = 'updatepropsfalse'; $objectTree = new Tree($dir); $this->server->tree = $objectTree; $props = array( '{http://sabredav.org/NS/test}someprop' => 'somevalue', ); $result = $this->server->updateProperties('/',$props); $this->assertEquals(array( '{http://sabredav.org/NS/test}someprop' => 403, ), $result); } /** * @depends testUpdateProperties */ public function testUpdatePropertiesFail2() { $dir = new Mock\PropertiesCollection('root', []); $dir->failMode = 'updatepropsarray'; $objectTree = new Tree($dir); $this->server->tree = $objectTree; $props = array( '{http://sabredav.org/NS/test}someprop' => 'somevalue', ); $result = $this->server->updateProperties('/',$props); $this->assertEquals(array( '{http://sabredav.org/NS/test}someprop' => 402 ), $result); } /** * @depends testUpdateProperties * @expectedException \UnexpectedValueException */ public function testUpdatePropertiesFail3() { $dir = new Mock\PropertiesCollection('root', []); $dir->failMode = 'updatepropsobj'; $objectTree = new Tree($dir); $this->server->tree = $objectTree; $props = array( '{http://sabredav.org/NS/test}someprop' => 'somevalue', ); $result = $this->server->updateProperties('/',$props); } /** * @depends testParsePropPatchRequest * @depends testUpdateProperties */ public function testPropPatch() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'PROPPATCH', ); $body = '<?xml version="1.0"?> <d:propertyupdate xmlns:d="DAV:" xmlns:s="http://www.rooftopsolutions.nl/testnamespace"> <d:set><d:prop><s:someprop>somevalue</s:someprop></d:prop></d:set> </d:propertyupdate>'; $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($body); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'Vary' => ['Brief,Prefer'], ), $this->response->getHeaders() ); $this->assertEquals(207, $this->response->status,'We got the wrong status. Full XML response: ' . $this->response->body); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop'); $this->assertEquals(1,count($data),'We expected one \'d:prop\' element. Response body: ' . $body); $data = $xml->xpath('//bla:someprop'); $this->assertEquals(1,count($data),'We expected one \'s:someprop\' element. Response body: ' . $body); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:status'); $this->assertEquals(1,count($data),'We expected one \'s:status\' element. Response body: ' . $body); $this->assertEquals('HTTP/1.1 200 OK',(string)$data[0]); } /** * @depends testPropPatch */ public function testPropPatchAndFetch() { $this->testPropPatch(); $xml = '<?xml version="1.0"?> <d:propfind xmlns:d="DAV:" xmlns:s="http://www.rooftopsolutions.nl/testnamespace"> <d:prop> <s:someprop /> </d:prop> </d:propfind>'; $this->sendRequest($xml); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); $xml->registerXPathNamespace('bla','http://www.rooftopsolutions.nl/testnamespace'); $xpath='//bla:someprop'; $result = $xml->xpath($xpath); $this->assertEquals(1,count($result),'We couldn\'t find our new property in the response. Full response body:' . "\n" . $body); $this->assertEquals('somevalue',(string)$result[0],'We couldn\'t find our new property in the response. Full response body:' . "\n" . $body); } } ���������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerRangeTest.php������������������������������������������������0000664�0000000�0000000�00000021415�12670356754�0022065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAV/AbstractServer.php'; class ServerRangeTest extends AbstractServer{ protected function getRootNode() { return new FSExt\Directory(SABRE_TEMPDIR); } function testRange() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=2-5', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [4], 'Content-Range' => ['bytes 2-5/13'], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')). '"'], ), $this->response->getHeaders() ); $this->assertEquals(206, $this->response->status); $this->assertEquals('st c', stream_get_contents($this->response->body)); } /** * @depends testRange */ function testStartRange() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=2-', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [11], 'Content-Range' => ['bytes 2-12/13'], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"'], ), $this->response->getHeaders() ); $this->assertEquals(206, $this->response->status); $this->assertEquals('st contents', stream_get_contents($this->response->body)); } /** * @depends testRange */ function testEndRange() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=-8', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [8], 'Content-Range' => ['bytes 5-12/13'], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')). '"'], ), $this->response->getHeaders() ); $this->assertEquals(206, $this->response->status); $this->assertEquals('contents', stream_get_contents($this->response->body)); } /** * @depends testRange */ function testTooHighRange() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=100-200', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(416, $this->response->status); } /** * @depends testRange */ function testCrazyRange() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=8-4', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(416, $this->response->status); } /** * @depends testRange */ function testIfRangeEtag() { $node = $this->server->tree->getNodeForPath('test.txt'); $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=2-5', 'HTTP_IF_RANGE' => $node->getETag(), ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [4], 'Content-Range' => ['bytes 2-5/13'], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"'], ), $this->response->getHeaders() ); $this->assertEquals(206, $this->response->status); $this->assertEquals('st c', stream_get_contents($this->response->body)); } /** * @depends testRange */ function testIfRangeEtagIncorrect() { $node = $this->server->tree->getNodeForPath('test.txt'); $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=2-5', 'HTTP_IF_RANGE' => $node->getETag() . 'blabla', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"'], ), $this->response->getHeaders() ); $this->assertEquals(200, $this->response->status); $this->assertEquals('Test contents', stream_get_contents($this->response->body)); } /** * @depends testRange */ function testIfRangeModificationDate() { $node = $this->server->tree->getNodeForPath('test.txt'); $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=2-5', 'HTTP_IF_RANGE' => 'tomorrow', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [4], 'Content-Range' => ['bytes 2-5/13'], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"'], ), $this->response->getHeaders() ); $this->assertEquals(206, $this->response->status); $this->assertEquals('st c', stream_get_contents($this->response->body)); } /** * @depends testRange */ function testIfRangeModificationDateModified() { $node = $this->server->tree->getNodeForPath('test.txt'); $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'HTTP_RANGE' => 'bytes=2-5', 'HTTP_IF_RANGE' => '-2 years', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], 'ETag' => ['"' . md5(file_get_contents(SABRE_TEMPDIR . '/test.txt')) . '"'], ), $this->response->getHeaders() ); $this->assertEquals(200, $this->response->status); $this->assertEquals('Test contents', stream_get_contents($this->response->body)); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerSimpleTest.php�����������������������������������������������0000664�0000000�0000000�00000042412�12670356754�0022262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; class ServerSimpleTest extends AbstractServer{ function testConstructArray() { $nodes = array( new SimpleCollection('hello') ); $server = new Server($nodes); $this->assertEquals($nodes[0], $server->tree->getNodeForPath('hello')); } /** * @expectedException Sabre\DAV\Exception */ function testConstructIncorrectObj() { $nodes = array( new SimpleCollection('hello'), new \STDClass(), ); $server = new Server($nodes); } /** * @expectedException Sabre\DAV\Exception */ function testConstructInvalidArg() { $server = new Server(1); } function testGet() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], ), $this->response->getHeaders() ); $this->assertEquals(200, $this->response->status); $this->assertEquals('Test contents', stream_get_contents($this->response->body)); } function testGetHttp10() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'GET', 'SERVER_PROTOCOL' => 'HTTP/1.0', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], ), $this->response->getHeaders() ); $this->assertEquals(200, $this->response->status); $this->assertEquals('1.0', $this->response->getHttpVersion()); $this->assertEquals('Test contents', stream_get_contents($this->response->body)); } function testGetDoesntExist() { $serverVars = array( 'REQUEST_URI' => '/test.txt_randomblbla', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(404, $this->response->status); } function testGetDoesntExist2() { $serverVars = array( 'REQUEST_URI' => '/test.txt/randomblbla', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(404, $this->response->status); } /** * This test should have the exact same result as testGet. * * The idea is that double slashes // are converted to single ones / * */ function testGetDoubleSlash() { $serverVars = array( 'REQUEST_URI' => '//test.txt', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], ), $this->response->getHeaders() ); $this->assertEquals(200, $this->response->status); $this->assertEquals('Test contents', stream_get_contents($this->response->body)); } function testHEAD() { $serverVars = array( 'REQUEST_URI' => '/test.txt', 'REQUEST_METHOD' => 'HEAD', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], ), $this->response->getHeaders() ); $this->assertEquals(200, $this->response->status); $this->assertEquals('', $this->response->body); } function testOptions() { $request = new HTTP\Request('OPTIONS', '/'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(array( 'DAV' => ['1, 3, extended-mkcol'], 'MS-Author-Via' => ['DAV'], 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT'], 'Accept-Ranges' => ['bytes'], 'Content-Length' => ['0'], 'X-Sabre-Version' => [Version::VERSION], ),$this->response->getHeaders()); $this->assertEquals(200, $this->response->status); $this->assertEquals('', $this->response->body); } function testOptionsUnmapped() { $request = new HTTP\Request('OPTIONS', '/unmapped'); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(array( 'DAV' => ['1, 3, extended-mkcol'], 'MS-Author-Via' => ['DAV'], 'Allow' => ['OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT, MKCOL'], 'Accept-Ranges' => ['bytes'], 'Content-Length' => ['0'], 'X-Sabre-Version' => [Version::VERSION], ),$this->response->getHeaders()); $this->assertEquals(200, $this->response->status); $this->assertEquals('', $this->response->body); } function testNonExistantMethod() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'BLABLA', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(501, $this->response->status); } function testGETOnCollection() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(501, $this->response->status); } function testHEADOnCollection() { $request = new HTTP\Request('HEAD', '/'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(200, $this->response->status); } function testBaseUri() { $serverVars = array( 'REQUEST_URI' => '/blabla/test.txt', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->setBaseUri('/blabla/'); $this->assertEquals('/blabla/',$this->server->getBaseUri()); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/octet-stream'], 'Content-Length' => [13], 'Last-Modified' => [HTTP\Util::toHTTPDate(new \DateTime('@' . filemtime($this->tempDir . '/test.txt')))], ), $this->response->getHeaders() ); $this->assertEquals(200, $this->response->status); $this->assertEquals('Test contents', stream_get_contents($this->response->body)); } function testBaseUriAddSlash() { $tests = array( '/' => '/', '/foo' => '/foo/', '/foo/' => '/foo/', '/foo/bar' => '/foo/bar/', '/foo/bar/' => '/foo/bar/', ); foreach($tests as $test=>$result) { $this->server->setBaseUri($test); $this->assertEquals($result, $this->server->getBaseUri()); } } function testCalculateUri() { $uris = array( 'http://www.example.org/root/somepath', '/root/somepath', '/root/somepath/', ); $this->server->setBaseUri('/root/'); foreach($uris as $uri) { $this->assertEquals('somepath',$this->server->calculateUri($uri)); } $this->server->setBaseUri('/root'); foreach($uris as $uri) { $this->assertEquals('somepath',$this->server->calculateUri($uri)); } $this->assertEquals('', $this->server->calculateUri('/root')); } function testCalculateUriSpecialChars() { $uris = array( 'http://www.example.org/root/%C3%A0fo%C3%B3', '/root/%C3%A0fo%C3%B3', '/root/%C3%A0fo%C3%B3/' ); $this->server->setBaseUri('/root/'); foreach($uris as $uri) { $this->assertEquals("\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri)); } $this->server->setBaseUri('/root'); foreach($uris as $uri) { $this->assertEquals("\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri)); } $this->server->setBaseUri('/'); foreach($uris as $uri) { $this->assertEquals("root/\xc3\xa0fo\xc3\xb3",$this->server->calculateUri($uri)); } } /** * @expectedException \Sabre\DAV\Exception\Forbidden */ function testBaseUriCheck() { $uris = array( 'http://www.example.org/root/somepath', '/root/somepath', '/root/somepath/' ); $this->server->setBaseUri('root/'); $this->server->calculateUri('/root/testuri'); $this->fail('Expected an exception'); } /** */ function testGuessBaseUri() { $serverVars = array( 'REQUEST_URI' => '/index.php/root', 'PATH_INFO' => '/root', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals('/index.php/', $server->guessBaseUri()); } /** * @depends testGuessBaseUri */ function testGuessBaseUriPercentEncoding() { $serverVars = array( 'REQUEST_URI' => '/index.php/dir/path2/path%20with%20spaces', 'PATH_INFO' => '/dir/path2/path with spaces', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals('/index.php/', $server->guessBaseUri()); } /** * @depends testGuessBaseUri */ /* function testGuessBaseUriPercentEncoding2() { $this->markTestIncomplete('This behaviour is not yet implemented'); $serverVars = array( 'REQUEST_URI' => '/some%20directory+mixed/index.php/dir/path2/path%20with%20spaces', 'PATH_INFO' => '/dir/path2/path with spaces', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals('/some%20directory+mixed/index.php/', $server->guessBaseUri()); }*/ function testGuessBaseUri2() { $serverVars = array( 'REQUEST_URI' => '/index.php/root/', 'PATH_INFO' => '/root/', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals('/index.php/', $server->guessBaseUri()); } function testGuessBaseUriNoPathInfo() { $serverVars = array( 'REQUEST_URI' => '/index.php/root', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals('/', $server->guessBaseUri()); } function testGuessBaseUriNoPathInfo2() { $serverVars = array( 'REQUEST_URI' => '/a/b/c/test.php', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals('/', $server->guessBaseUri()); } /** * @depends testGuessBaseUri */ function testGuessBaseUriQueryString() { $serverVars = array( 'REQUEST_URI' => '/index.php/root?query_string=blabla', 'PATH_INFO' => '/root', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $server = new Server(); $server->httpRequest = $httpRequest; $this->assertEquals('/index.php/', $server->guessBaseUri()); } /** * @depends testGuessBaseUri * @expectedException \Sabre\DAV\Exception */ function testGuessBaseUriBadConfig() { $serverVars = array( 'REQUEST_URI' => '/index.php/root/heyyy', 'PATH_INFO' => '/root', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $server = new Server(); $server->httpRequest = $httpRequest; $server->guessBaseUri(); } function testTriggerException() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'FOO', ); $httpRequest = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = $httpRequest; $this->server->on('beforeMethod', [$this,'exceptionTrigger']); $this->server->exec(); $this->assertEquals(array( 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $this->assertEquals(500, $this->response->status); } function exceptionTrigger() { throw new Exception('Hola'); } function testReportNotFound() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'REPORT', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->httpRequest->setBody('<?xml version="1.0"?><bla:myreport xmlns:bla="http://www.rooftopsolutions.nl/NS"></bla:myreport>'); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $this->response->getHeaders() ); $this->assertEquals(415, $this->response->status, 'We got an incorrect status back. Full response body follows: ' . $this->response->body); } function testReportIntercepted() { $serverVars = array( 'REQUEST_URI' => '/', 'REQUEST_METHOD' => 'REPORT', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->httpRequest->setBody('<?xml version="1.0"?><bla:myreport xmlns:bla="http://www.rooftopsolutions.nl/NS"></bla:myreport>'); $this->server->on('report', [$this,'reportHandler']); $this->server->exec(); $this->assertEquals(array( 'X-Sabre-Version' => [Version::VERSION], 'testheader' => ['testvalue'], ), $this->response->getHeaders() ); $this->assertEquals(418, $this->response->status,'We got an incorrect status back. Full response body follows: ' . $this->response->body); } function reportHandler($reportName) { if ($reportName=='{http://www.rooftopsolutions.nl/NS}myreport') { $this->server->httpResponse->setStatus(418); $this->server->httpResponse->setHeader('testheader','testvalue'); return false; } else return; } function testGetPropertiesForChildren() { $result = $this->server->getPropertiesForChildren('',array( '{DAV:}getcontentlength', )); $expected = array( 'test.txt' => array('{DAV:}getcontentlength' => 13), 'dir/' => array(), ); $this->assertEquals($expected,$result); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/ServerUpdatePropertiesTest.php�������������������������������������0000664�0000000�0000000�00000006044�12670356754�0024331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; class ServerUpdatePropertiesTest extends \PHPUnit_Framework_TestCase { function testUpdatePropertiesFail() { $tree = array( new SimpleCollection('foo'), ); $server = new Server($tree); $result = $server->updateProperties('foo', array( '{DAV:}foo' => 'bar' )); $expected = array( '{DAV:}foo' => 403, ); $this->assertEquals($expected, $result); } function testUpdatePropertiesProtected() { $tree = array( new SimpleCollection('foo'), ); $server = new Server($tree); $server->on('propPatch', function($path, PropPatch $propPatch) { $propPatch->handleRemaining(function() { return true; }); }); $result = $server->updateProperties('foo', array( '{DAV:}getetag' => 'bla', '{DAV:}foo' => 'bar' )); $expected = array( '{DAV:}getetag' => 403, '{DAV:}foo' => 424, ); $this->assertEquals($expected, $result); } function testUpdatePropertiesEventFail() { $tree = array( new SimpleCollection('foo'), ); $server = new Server($tree); $server->on('propPatch', function($path, PropPatch $propPatch) { $propPatch->setResultCode('{DAV:}foo', 404); $propPatch->handleRemaining(function() { return true; }); }); $result = $server->updateProperties('foo', array( '{DAV:}foo' => 'bar', '{DAV:}foo2' => 'bla', )); $expected = array( '{DAV:}foo' => 404, '{DAV:}foo2' => 424, ); $this->assertEquals($expected, $result); } function updatePropFail(&$propertyDelta, &$result, $node) { $result[404] = array( '{DAV:}foo' => null, ); unset($propertyDelta['{DAV:}foo']); return false; } function testUpdatePropertiesEventSuccess() { $tree = array( new SimpleCollection('foo'), ); $server = new Server($tree); $server->on('propPatch', function($path, PropPatch $propPatch) { $propPatch->handle(['{DAV:}foo', '{DAV:}foo2'], function() { return [ '{DAV:}foo' => 200, '{DAV:}foo2' => 201, ]; }); }); $result = $server->updateProperties('foo', array( '{DAV:}foo' => 'bar', '{DAV:}foo2' => 'bla', )); $expected = array( '{DAV:}foo' => 200, '{DAV:}foo2' => 201, ); $this->assertEquals($expected, $result); } function updatePropSuccess(&$propertyDelta, &$result, $node) { $result[200] = array( '{DAV:}foo' => null, ); $result[201] = array( '{DAV:}foo2' => null, ); unset($propertyDelta['{DAV:}foo']); unset($propertyDelta['{DAV:}foo2']); return; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/SimpleFileTest.php�������������������������������������������������0000664�0000000�0000000�00000001000�12670356754�0021657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class SimpleFileTest extends \PHPUnit_Framework_TestCase { function testAll() { $file = new SimpleFile('filename.txt','contents','text/plain'); $this->assertEquals('filename.txt', $file->getName()); $this->assertEquals('contents', $file->get()); $this->assertEquals('8', $file->getSize()); $this->assertEquals('"' . md5('contents') . '"', $file->getETag()); $this->assertEquals('text/plain', $file->getContentType()); } } sabre-dav-2.1.10/tests/Sabre/DAV/StringUtilTest.php�������������������������������������������������0000664�0000000�0000000�00000011131�12670356754�0021740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class StringUtilTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider dataset */ function testTextMatch($haystack, $needle, $collation, $matchType, $result) { $this->assertEquals($result, StringUtil::textMatch($haystack, $needle, $collation, $matchType)); } function dataset() { return array( array('FOOBAR', 'FOO', 'i;octet', 'contains', true), array('FOOBAR', 'foo', 'i;octet', 'contains', false), array('FÖÖBAR', 'FÖÖ', 'i;octet', 'contains', true), array('FÖÖBAR', 'föö', 'i;octet', 'contains', false), array('FOOBAR', 'FOOBAR', 'i;octet', 'equals', true), array('FOOBAR', 'fooBAR', 'i;octet', 'equals', false), array('FOOBAR', 'FOO', 'i;octet', 'starts-with', true), array('FOOBAR', 'foo', 'i;octet', 'starts-with', false), array('FOOBAR', 'BAR', 'i;octet', 'starts-with', false), array('FOOBAR', 'bar', 'i;octet', 'starts-with', false), array('FOOBAR', 'FOO', 'i;octet', 'ends-with', false), array('FOOBAR', 'foo', 'i;octet', 'ends-with', false), array('FOOBAR', 'BAR', 'i;octet', 'ends-with', true), array('FOOBAR', 'bar', 'i;octet', 'ends-with', false), array('FOOBAR', 'FOO', 'i;ascii-casemap', 'contains', true), array('FOOBAR', 'foo', 'i;ascii-casemap', 'contains', true), array('FÖÖBAR', 'FÖÖ', 'i;ascii-casemap', 'contains', true), array('FÖÖBAR', 'föö', 'i;ascii-casemap', 'contains', false), array('FOOBAR', 'FOOBAR', 'i;ascii-casemap', 'equals', true), array('FOOBAR', 'fooBAR', 'i;ascii-casemap', 'equals', true), array('FOOBAR', 'FOO', 'i;ascii-casemap', 'starts-with', true), array('FOOBAR', 'foo', 'i;ascii-casemap', 'starts-with', true), array('FOOBAR', 'BAR', 'i;ascii-casemap', 'starts-with', false), array('FOOBAR', 'bar', 'i;ascii-casemap', 'starts-with', false), array('FOOBAR', 'FOO', 'i;ascii-casemap', 'ends-with', false), array('FOOBAR', 'foo', 'i;ascii-casemap', 'ends-with', false), array('FOOBAR', 'BAR', 'i;ascii-casemap', 'ends-with', true), array('FOOBAR', 'bar', 'i;ascii-casemap', 'ends-with', true), array('FOOBAR', 'FOO', 'i;unicode-casemap', 'contains', true), array('FOOBAR', 'foo', 'i;unicode-casemap', 'contains', true), array('FÖÖBAR', 'FÖÖ', 'i;unicode-casemap', 'contains', true), array('FÖÖBAR', 'föö', 'i;unicode-casemap', 'contains', true), array('FOOBAR', 'FOOBAR', 'i;unicode-casemap', 'equals', true), array('FOOBAR', 'fooBAR', 'i;unicode-casemap', 'equals', true), array('FOOBAR', 'FOO', 'i;unicode-casemap', 'starts-with', true), array('FOOBAR', 'foo', 'i;unicode-casemap', 'starts-with', true), array('FOOBAR', 'BAR', 'i;unicode-casemap', 'starts-with', false), array('FOOBAR', 'bar', 'i;unicode-casemap', 'starts-with', false), array('FOOBAR', 'FOO', 'i;unicode-casemap', 'ends-with', false), array('FOOBAR', 'foo', 'i;unicode-casemap', 'ends-with', false), array('FOOBAR', 'BAR', 'i;unicode-casemap', 'ends-with', true), array('FOOBAR', 'bar', 'i;unicode-casemap', 'ends-with', true), ); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ public function testBadCollation() { StringUtil::textMatch('foobar','foo','blabla','contains'); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ public function testBadMatchType() { StringUtil::textMatch('foobar','foo','i;octet','booh'); } public function testEnsureUTF8_ascii() { $inputString = "harkema"; $outputString = "harkema"; $this->assertEquals( $outputString, StringUtil::ensureUTF8($inputString) ); } public function testEnsureUTF8_latin1() { $inputString = "m\xfcnster"; $outputString = "münster"; $this->assertEquals( $outputString, StringUtil::ensureUTF8($inputString) ); } public function testEnsureUTF8_utf8() { $inputString = "m\xc3\xbcnster"; $outputString = "münster"; $this->assertEquals( $outputString, StringUtil::ensureUTF8($inputString) ); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Sync/��������������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0017202�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Sync/MockSyncCollection.php����������������������������������������0000664�0000000�0000000�00000012105�12670356754�0023454�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Sync; use Sabre\DAV; /** * This mocks a ISyncCollection, for unittesting. * * This object behaves the same as SimpleCollection. Call addChange to update * the 'changelog' that this class uses for the collection. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class MockSyncCollection extends DAV\SimpleCollection implements ISyncCollection { public $changeLog = []; public $token = null; /** * This method returns the current sync-token for this collection. * This can be any string. * * If null is returned from this function, the plugin assumes there's no * sync information available. * * @return string|null */ public function getSyncToken() { // Will be 'null' in the first round, and will increment ever after. return $this->token; } public function addChange(array $added, array $modified, array $deleted) { $this->token++; $this->changeLog[$this->token] = [ 'added' => $added, 'modified' => $modified, 'deleted' => $deleted, ]; } /** * The getChanges method returns all the changes that have happened, since * the specified syncToken and the current collection. * * This function should return an array, such as the following: * * array( * 'syncToken' => 'The current synctoken', * 'modified' => array( * 'new.txt', * ), * 'deleted' => array( * 'foo.php.bak', * 'old.txt' * ) * ); * * The syncToken property should reflect the *current* syncToken of the * collection, as reported getSyncToken(). This is needed here too, to * ensure the operation is atomic. * * If the syncToken is specified as null, this is an initial sync, and all * members should be reported. * * The modified property is an array of nodenames that have changed since * the last token. * * The deleted property is an array with nodenames, that have been deleted * from collection. * * The second argument is basically the 'depth' of the report. If it's 1, * you only have to report changes that happened only directly in immediate * descendants. If it's 2, it should also include changes from the nodes * below the child collections. (grandchildren) * * The third (optional) argument allows a client to specify how many * results should be returned at most. If the limit is not specified, it * should be treated as infinite. * * If the limit (infinite or not) is higher than you're willing to return, * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. * * If the syncToken is expired (due to data cleanup) or unknown, you must * return null. * * The limit is 'suggestive'. You are free to ignore it. * * @param string $syncToken * @param int $syncLevel * @param int $limit * @return array */ public function getChanges($syncToken, $syncLevel, $limit = null) { // This is an initial sync if (is_null($syncToken)) { return [ 'added' => array_map( function($item) { return $item->getName(); }, $this->getChildren() ), 'modified' => [], 'deleted' => [], 'syncToken' => $this->getSyncToken(), ]; } if (!is_int($syncToken) && !ctype_digit($syncToken)) { return null; } if (is_null($this->token)) return null; $added = []; $modified = []; $deleted = []; foreach($this->changeLog as $token=>$change) { if ($token > $syncToken) { $added = array_merge($added, $change['added']); $modified = array_merge($modified, $change['modified']); $deleted = array_merge($deleted, $change['deleted']); if ($limit) { // If there's a limit, we may need to cut things off. // This alghorithm is weird and stupid, but it works. $left = $limit - (count($modified) + count($deleted)); if ($left>0) continue; if ($left===0) break; if ($left<0) { $modified = array_slice($modified, 0, $left); } $left = $limit - (count($modified) + count($deleted)); if ($left===0) break; if ($left<0) { $deleted = array_slice($deleted, 0, $left); } break; } } } return array( 'syncToken' => $this->token, 'added' => $added, 'modified' => $modified, 'deleted' => $deleted, ); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/Sync/PluginTest.php������������������������������������������������0000664�0000000�0000000�00000037623�12670356754�0022024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV\Sync; use Sabre\DAV, Sabre\HTTP; require_once __DIR__ . '/MockSyncCollection.php'; class PluginTest extends \Sabre\DAVServerTest { protected $collection; public function setUp() { parent::setUp(); $this->server->addPlugin(new Plugin()); } public function setUpTree() { $this->collection = new MockSyncCollection('coll', [ new DAV\SimpleFile('file1.txt','foo'), new DAV\SimpleFile('file2.txt','bar'), ]); $this->tree = [ $this->collection, new DAV\SimpleCollection('normalcoll', []) ]; } public function testSupportedReportSet() { $result = $this->server->getProperties('/coll', ['{DAV:}supported-report-set']); $this->assertFalse($result['{DAV:}supported-report-set']->has('{DAV:}sync-collection')); // Making a change $this->collection->addChange(['file1.txt'], [], []); $result = $this->server->getProperties('/coll', ['{DAV:}supported-report-set']); $this->assertTrue($result['{DAV:}supported-report-set']->has('{DAV:}sync-collection')); } public function testGetSyncToken() { $result = $this->server->getProperties('/coll', ['{DAV:}sync-token']); $this->assertFalse(isset($result['{DAV:}sync-token'])); // Making a change $this->collection->addChange(['file1.txt'], [], []); $result = $this->server->getProperties('/coll', ['{DAV:}sync-token']); $this->assertTrue(isset($result['{DAV:}sync-token'])); // non-sync-enabled collection $this->collection->addChange(['file1.txt'], [], []); $result = $this->server->getProperties('/normalcoll', ['{DAV:}sync-token']); $this->assertFalse(isset($result['{DAV:}sync-token'])); } public function testSyncInitialSyncCollection() { // Making a change $this->collection->addChange(['file1.txt'], [], []); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token/> <D:sync-level>1</D:sync-level> <D:prop> <D:getcontentlength/> </D:prop> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); $dom = DAV\XMLUtil::loadDOMDocument( $response->body ); // Checking the sync-token $this->assertEquals( 'http://sabre.io/ns/sync/1', $dom->getElementsByTagNameNS('urn:DAV', 'sync-token')->item(0)->nodeValue ); $responses = DAV\Property\ResponseList::unserialize( $dom->documentElement, [] ); $responses = $responses->getResponses(); $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); $response = $responses[0]; $this->assertNull($response->getHttpStatus()); $this->assertEquals('/coll/file1.txt', $response->getHref()); $this->assertEquals([ 200 => [ '{DAV:}getcontentlength' => 3, ] ], $response->getResponseProperties()); $response = $responses[1]; $this->assertNull($response->getHttpStatus()); $this->assertEquals('/coll/file2.txt', $response->getHref()); $this->assertEquals([ 200 => [ '{DAV:}getcontentlength' => 3, ] ], $response->getResponseProperties()); } public function testSubsequentSyncSyncCollection() { // Making a change $this->collection->addChange(['file1.txt'], [], []); // Making another change $this->collection->addChange([], ['file2.txt'], ['file3.txt']); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> <D:sync-level>infinite</D:sync-level> <D:prop> <D:getcontentlength/> </D:prop> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); $dom = DAV\XMLUtil::loadDOMDocument( $response->body ); // Checking the sync-token $this->assertEquals( 'http://sabre.io/ns/sync/2', $dom->getElementsByTagNameNS('urn:DAV', 'sync-token')->item(0)->nodeValue ); $responses = DAV\Property\ResponseList::unserialize( $dom->documentElement, [] ); $responses = $responses->getResponses(); $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); $response = $responses[0]; $this->assertNull($response->getHttpStatus()); $this->assertEquals('/coll/file2.txt', $response->getHref()); $this->assertEquals([ 200 => [ '{DAV:}getcontentlength' => 3, ] ], $response->getResponseProperties()); $response = $responses[1]; $this->assertEquals('404', $response->getHttpStatus()); $this->assertEquals('/coll/file3.txt', $response->getHref()); $this->assertEquals([], $response->getResponseProperties()); } public function testSubsequentSyncSyncCollectionLimit() { // Making a change $this->collection->addChange(['file1.txt'], [], []); // Making another change $this->collection->addChange([], ['file2.txt'], ['file3.txt']); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> <D:sync-level>infinite</D:sync-level> <D:prop> <D:getcontentlength/> </D:prop> <D:limit><D:nresults>1</D:nresults></D:limit> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); $dom = DAV\XMLUtil::loadDOMDocument( $response->body ); // Checking the sync-token $this->assertEquals( 'http://sabre.io/ns/sync/2', $dom->getElementsByTagNameNS('urn:DAV', 'sync-token')->item(0)->nodeValue ); $responses = DAV\Property\ResponseList::unserialize( $dom->documentElement, [] ); $responses = $responses->getResponses(); $this->assertEquals(1, count($responses), 'We expected exactly 1 {DAV:}response'); $response = $responses[0]; $this->assertEquals('404', $response->getHttpStatus()); $this->assertEquals('/coll/file3.txt', $response->getHref()); $this->assertEquals([], $response->getResponseProperties()); } public function testSubsequentSyncSyncCollectionDepthFallBack() { // Making a change $this->collection->addChange(['file1.txt'], [], []); // Making another change $this->collection->addChange([], ['file2.txt'], ['file3.txt']); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', 'HTTP_DEPTH' => "1", ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token>http://sabre.io/ns/sync/1</D:sync-token> <D:prop> <D:getcontentlength/> </D:prop> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); $this->assertEquals(207, $response->status, 'Full response body:' . $response->body); $dom = DAV\XMLUtil::loadDOMDocument( $response->body ); // Checking the sync-token $this->assertEquals( 'http://sabre.io/ns/sync/2', $dom->getElementsByTagNameNS('urn:DAV', 'sync-token')->item(0)->nodeValue ); $responses = DAV\Property\ResponseList::unserialize( $dom->documentElement, [] ); $responses = $responses->getResponses(); $this->assertEquals(2, count($responses), 'We expected exactly 2 {DAV:}response'); $response = $responses[0]; $this->assertNull($response->getHttpStatus()); $this->assertEquals('/coll/file2.txt', $response->getHref()); $this->assertEquals([ 200 => [ '{DAV:}getcontentlength' => 3, ] ], $response->getResponseProperties()); $response = $responses[1]; $this->assertEquals('404', $response->getHttpStatus()); $this->assertEquals('/coll/file3.txt', $response->getHref()); $this->assertEquals([], $response->getResponseProperties()); } public function testSyncNoSyncInfo() { $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token/> <D:sync-level>1</D:sync-level> <D:prop> <D:getcontentlength/> </D:prop> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); // The default state has no sync-token, so this report should not yet // be supported. $this->assertEquals(415, $response->status, 'Full response body:' . $response->body); } public function testSyncNoSyncCollection() { $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/normalcoll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token/> <D:sync-level>1</D:sync-level> <D:prop> <D:getcontentlength/> </D:prop> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); // The default state has no sync-token, so this report should not yet // be supported. $this->assertEquals(415, $response->status, 'Full response body:' . $response->body); } public function testSyncInvalidToken() { $this->collection->addChange(['file1.txt'], [], []); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token>http://sabre.io/ns/sync/invalid</D:sync-token> <D:sync-level>1</D:sync-level> <D:prop> <D:getcontentlength/> </D:prop> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); // The default state has no sync-token, so this report should not yet // be supported. $this->assertEquals(403, $response->status, 'Full response body:' . $response->body); } public function testSyncInvalidTokenNoPrefix() { $this->collection->addChange(['file1.txt'], [], []); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token>invalid</D:sync-token> <D:sync-level>1</D:sync-level> <D:prop> <D:getcontentlength/> </D:prop> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); // The default state has no sync-token, so this report should not yet // be supported. $this->assertEquals(403, $response->status, 'Full response body:' . $response->body); } public function testSyncNoSyncToken() { $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-level>1</D:sync-level> <D:prop> <D:getcontentlength/> </D:prop> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); // The default state has no sync-token, so this report should not yet // be supported. $this->assertEquals(400, $response->status, 'Full response body:' . $response->body); } public function testSyncNoProp() { $this->collection->addChange(['file1.txt'], [], []); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'REPORT', 'REQUEST_URI' => '/coll/', 'CONTENT_TYPE' => 'application/xml', ]); $body = <<<BLA <?xml version="1.0" encoding="utf-8" ?> <D:sync-collection xmlns:D="DAV:"> <D:sync-token /> <D:sync-level>1</D:sync-level> </D:sync-collection> BLA; $request->setBody($body); $response = $this->request($request); // The default state has no sync-token, so this report should not yet // be supported. $this->assertEquals(400, $response->status, 'Full response body:' . $response->body); } public function testIfConditions() { $this->collection->addChange(['file1.txt'], [], []); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'DELETE', 'REQUEST_URI' => '/coll/file1.txt', 'HTTP_IF' => '</coll> (<http://sabre.io/ns/sync/1>)', ]); $response = $this->request($request); // If a 403 is thrown this works correctly. The file in questions // doesn't allow itself to be deleted. // If the If conditions failed, it would have been a 412 instead. $this->assertEquals(403, $response->status); } public function testIfConditionsNot() { $this->collection->addChange(['file1.txt'], [], []); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'DELETE', 'REQUEST_URI' => '/coll/file1.txt', 'HTTP_IF' => '</coll> (Not <http://sabre.io/ns/sync/2>)', ]); $response = $this->request($request); // If a 403 is thrown this works correctly. The file in questions // doesn't allow itself to be deleted. // If the If conditions failed, it would have been a 412 instead. $this->assertEquals(403, $response->status); } public function testIfConditionsNoSyncToken() { $this->collection->addChange(['file1.txt'], [], []); $request = HTTP\Sapi::createFromServerArray([ 'REQUEST_METHOD' => 'DELETE', 'REQUEST_URI' => '/coll/file1.txt', 'HTTP_IF' => '</coll> (<opaquelocktoken:foo>)', ]); $response = $this->request($request); $this->assertEquals(412, $response->status); } } �������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/SyncTokenPropertyTest.php������������������������������������������0000664�0000000�0000000�00000005022�12670356754�0023320�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class SyncTokenPropertyTest extends \Sabre\DAVServerTest { /** * The assumption in these tests is that a PROPFIND is going on, and to * fetch the sync-token, the event handler is just able to use the existing * result. * * @dataProvider data */ function testAlreadyThere1($name, $value) { $propFind = new PropFind('foo', [ '{http://calendarserver.org/ns/}getctag', $name, ]); $propFind->set($name, $value); $corePlugin = new CorePlugin(); $corePlugin->propFindLate($propFind, new SimpleCollection('hi')); $this->assertEquals("hello", $propFind->get('{http://calendarserver.org/ns/}getctag')); } /** * In these test-cases, the plugin is forced to do a local propfind to * fetch the items. * * @dataProvider data */ function testRefetch($name, $value) { $this->server->tree = new Tree( new SimpleCollection('root', [ new Mock\PropertiesCollection( 'foo', [], [$name => $value] ) ]) ); $propFind = new PropFind('foo', [ '{http://calendarserver.org/ns/}getctag', $name, ]); $corePlugin = $this->server->getPlugin('core'); $corePlugin->propFindLate($propFind, new SimpleCollection('hi')); $this->assertEquals("hello", $propFind->get('{http://calendarserver.org/ns/}getctag')); } function testNoData() { $this->server->tree = new Tree( new SimpleCollection('root', [ new Mock\PropertiesCollection( 'foo', [], [] ) ]) ); $propFind = new PropFind('foo', [ '{http://calendarserver.org/ns/}getctag', ]); $corePlugin = $this->server->getPlugin('core'); $corePlugin->propFindLate($propFind, new SimpleCollection('hi')); $this->assertNull($propFind->get('{http://calendarserver.org/ns/}getctag')); } function data() { return [ [ '{http://sabredav.org/ns}sync-token', "hello" ], [ '{DAV:}sync-token', "hello" ], [ '{DAV:}sync-token', new Property\Href(Sync\Plugin::SYNCTOKEN_PREFIX . "hello", false) ] ]; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/TemporaryFileFilterTest.php����������������������������������������0000664�0000000�0000000�00000020610�12670356754�0023566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP; class TemporaryFileFilterTest extends AbstractServer { function setUp() { parent::setUp(); $plugin = new TemporaryFileFilterPlugin(SABRE_TEMPDIR . '/tff'); $this->server->addPlugin($plugin); } function testPutNormal() { $serverVars = array( 'REQUEST_URI' => '/testput.txt', 'REQUEST_METHOD' => 'PUT', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('Testing new file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals('', $this->response->body); $this->assertEquals(201, $this->response->status); $this->assertEquals('0', $this->response->getHeader('Content-Length')); $this->assertEquals('Testing new file',file_get_contents(SABRE_TEMPDIR . '/testput.txt')); } function testPutTemp() { // mimicking an OS/X resource fork $serverVars = array( 'REQUEST_URI' => '/._testput.txt', 'REQUEST_METHOD' => 'PUT', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('Testing new file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals('', $this->response->body); $this->assertEquals(201, $this->response->status); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], ),$this->response->getHeaders()); $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testput.txt'),'._testput.txt should not exist in the regular file structure.'); } function testPutTempIfNoneMatch() { // mimicking an OS/X resource fork $serverVars = array( 'REQUEST_URI' => '/._testput.txt', 'REQUEST_METHOD' => 'PUT', 'HTTP_IF_NONE_MATCH' => '*', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('Testing new file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals('', $this->response->body); $this->assertEquals(201, $this->response->status); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], ),$this->response->getHeaders()); $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testput.txt'),'._testput.txt should not exist in the regular file structure.'); $this->server->exec(); $this->assertEquals(412, $this->response->status); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); } function testPutGet() { // mimicking an OS/X resource fork $serverVars = array( 'REQUEST_URI' => '/._testput.txt', 'REQUEST_METHOD' => 'PUT', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('Testing new file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals('', $this->response->body); $this->assertEquals(201, $this->response->status); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], ),$this->response->getHeaders()); $serverVars = array( 'REQUEST_URI' => '/._testput.txt', 'REQUEST_METHOD' => 'GET', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(200, $this->response->status); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], 'Content-Length' => [16], 'Content-Type' => ['application/octet-stream'], ),$this->response->getHeaders()); $this->assertEquals('Testing new file',stream_get_contents($this->response->body)); } function testLockNonExistant() { mkdir(SABRE_TEMPDIR . '/locksdir'); $locksBackend = new Locks\Backend\FS(SABRE_TEMPDIR . '/locksdir'); $locksPlugin = new Locks\Plugin($locksBackend); $this->server->addPlugin($locksPlugin); // mimicking an OS/X resource fork $serverVars = array( 'REQUEST_URI' => '/._testlock.txt', 'REQUEST_METHOD' => 'LOCK', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('<?xml version="1.0"?> <D:lockinfo xmlns:D="DAV:"> <D:lockscope><D:exclusive/></D:lockscope> <D:locktype><D:write/></D:locktype> <D:owner> <D:href>http://example.org/~ejw/contact.html</D:href> </D:owner> </D:lockinfo>'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(201, $this->response->status); $this->assertEquals('application/xml; charset=utf-8',$this->response->getHeader('Content-Type')); $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/',$this->response->getHeader('Lock-Token'))===1,'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')'); $this->assertEquals('true',$this->response->getHeader('X-Sabre-Temp')); $this->assertFalse(file_exists(SABRE_TEMPDIR . '/._testlock.txt'),'._testlock.txt should not exist in the regular file structure.'); } function testPutDelete() { // mimicking an OS/X resource fork $serverVars = array( 'REQUEST_URI' => '/._testput.txt', 'REQUEST_METHOD' => 'PUT', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('Testing new file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals('', $this->response->body); $this->assertEquals(201, $this->response->status); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], ),$this->response->getHeaders()); $serverVars = array( 'REQUEST_URI' => '/._testput.txt', 'REQUEST_METHOD' => 'DELETE', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(204, $this->response->status, "Incorrect status code received. Full body:\n". $this->response->body); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], ),$this->response->getHeaders()); $this->assertEquals('',$this->response->body); } function testPutPropfind() { // mimicking an OS/X resource fork $serverVars = array( 'REQUEST_URI' => '/._testput.txt', 'REQUEST_METHOD' => 'PUT', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody('Testing new file'); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals('', $this->response->body); $this->assertEquals(201, $this->response->status); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], ),$this->response->getHeaders()); $serverVars = array( 'REQUEST_URI' => '/._testput.txt', 'REQUEST_METHOD' => 'PROPFIND', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody(''); $this->server->httpRequest = ($request); $this->server->exec(); $this->assertEquals(207, $this->response->status,'Incorrect status code returned. Body: ' . $this->response->body); $this->assertEquals(array( 'X-Sabre-Temp' => ['true'], 'Content-Type' => ['application/xml; charset=utf-8'], ),$this->response->getHeaders()); $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/","xmlns\\1=\"urn:DAV\"",$this->response->body); $xml = simplexml_load_string($body); $xml->registerXPathNamespace('d','urn:DAV'); list($data) = $xml->xpath('/d:multistatus/d:response/d:href'); $this->assertEquals('/._testput.txt',(string)$data,'href element should have been /._testput.txt'); $data = $xml->xpath('/d:multistatus/d:response/d:propstat/d:prop/d:resourcetype'); $this->assertEquals(1,count($data)); } } ������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/TestPlugin.php�����������������������������������������������������0000664�0000000�0000000�00000001122�12670356754�0021071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; use Sabre\HTTP\RequestInterface, Sabre\HTTP\ResponseInterface; class TestPlugin extends ServerPlugin { public $beforeMethod; function getFeatures() { return ['drinking']; } function getHTTPMethods($uri) { return ['BEER','WINE']; } function initialize(Server $server) { $server->on('beforeMethod', [$this,'beforeMethod']); } function beforeMethod(RequestInterface $request, ResponseInterface $response) { $this->beforeMethod = $request->getMethod(); return true; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/TreeTest.php�������������������������������������������������������0000664�0000000�0000000�00000012520�12670356754�0020536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class TreeTest extends \PHPUnit_Framework_TestCase { function testNodeExists() { $tree = new TreeMock(); $this->assertTrue($tree->nodeExists('hi')); $this->assertFalse($tree->nodeExists('hello')); } function testCopy() { $tree = new TreeMock(); $tree->copy('hi','hi2'); $this->assertArrayHasKey('hi2', $tree->getNodeForPath('')->newDirectories); $this->assertEquals('foobar', $tree->getNodeForPath('hi/file')->get()); $this->assertEquals(array('test1'=>'value'), $tree->getNodeForPath('hi/file')->getProperties(array())); } function testMove() { $tree = new TreeMock(); $tree->move('hi','hi2'); $this->assertEquals('hi2', $tree->getNodeForPath('hi')->getName()); $this->assertTrue($tree->getNodeForPath('hi')->isRenamed); } function testDeepMove() { $tree = new TreeMock(); $tree->move('hi/sub','hi2'); $this->assertArrayHasKey('hi2', $tree->getNodeForPath('')->newDirectories); $this->assertTrue($tree->getNodeForPath('hi/sub')->isDeleted); } function testDelete() { $tree = new TreeMock(); $tree->delete('hi'); $this->assertTrue($tree->getNodeForPath('hi')->isDeleted); } function testGetChildren() { $tree = new TreeMock(); $children = $tree->getChildren(''); $this->assertEquals(2,count($children)); $this->assertEquals('hi', $children[0]->getName()); } function testGetMultipleNodes() { $tree = new TreeMock(); $result = $tree->getMultipleNodes(['hi/sub', 'hi/file']); $this->assertArrayHasKey('hi/sub', $result); $this->assertArrayHasKey('hi/file', $result); $this->assertEquals('sub', $result['hi/sub']->getName()); $this->assertEquals('file', $result['hi/file']->getName()); } function testGetMultipleNodes2() { $tree = new TreeMock(); $result = $tree->getMultipleNodes(['multi/1', 'multi/2']); $this->assertArrayHasKey('multi/1', $result); $this->assertArrayHasKey('multi/2', $result); } } class TreeMock extends Tree { private $nodes = array(); function __construct() { $file = new TreeFileTester('file'); $file->properties = ['test1'=>'value']; $file->data = 'foobar'; parent::__construct( new TreeDirectoryTester('root', [ new TreeDirectoryTester('hi', [ new TreeDirectoryTester('sub'), $file, ]), new TreeMultiGetTester('multi', [ new TreeFileTester('1'), new TreeFileTester('2'), new TreeFileTester('3'), ]) ]) ); } } class TreeDirectoryTester extends SimpleCollection { public $newDirectories = array(); public $newFiles = array(); public $isDeleted = false; public $isRenamed = false; function createDirectory($name) { $this->newDirectories[$name] = true; } function createFile($name,$data = null) { $this->newFiles[$name] = $data; } function getChild($name) { if (isset($this->newDirectories[$name])) return new TreeDirectoryTester($name); if (isset($this->newFiles[$name])) return new TreeFileTester($name, $this->newFiles[$name]); return parent::getChild($name); } function childExists($name) { return !!$this->getChild($name); } function delete() { $this->isDeleted = true; } function setName($name) { $this->isRenamed = true; $this->name = $name; } } class TreeFileTester extends File implements IProperties { public $name; public $data; public $properties; function __construct($name, $data = null) { $this->name = $name; if (is_null($data)) $data = 'bla'; $this->data = $data; } function getName() { return $this->name; } function get() { return $this->data; } function getProperties($properties) { return $this->properties; } /** * Updates properties on this node. * * This method received a PropPatch object, which contains all the * information about the update. * * To update specific properties, call the 'handle' method on this object. * Read the PropPatch documentation for more information. * * @param array $mutations * @return bool|array */ function propPatch(PropPatch $propPatch) { $this->properties = $propPatch->getMutations(); $propPatch->setRemainingResultCode(200); } } class TreeMultiGetTester extends TreeDirectoryTester implements IMultiGet { /** * This method receives a list of paths in it's first argument. * It must return an array with Node objects. * * If any children are not found, you do not have to return them. * * @return array */ function getMultipleChildren(array $paths) { $result = []; foreach($paths as $path) { try { $child = $this->getChild($path); $result[] = $child; } catch (Exception\NotFound $e) { // Do nothing } } return $result; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/UUIDUtilTest.php���������������������������������������������������0000664�0000000�0000000�00000001114�12670356754�0021240�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class UUIDUtilTest extends \PHPUnit_Framework_TestCase { function testValidateUUID() { $this->assertTrue( UUIDUtil::validateUUID('11111111-2222-3333-4444-555555555555') ); $this->assertFalse( UUIDUtil::validateUUID(' 11111111-2222-3333-4444-555555555555') ); $this->assertTrue( UUIDUtil::validateUUID('ffffffff-2222-3333-4444-555555555555') ); $this->assertFalse( UUIDUtil::validateUUID('fffffffg-2222-3333-4444-555555555555') ); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAV/XMLUtilTest.php����������������������������������������������������0000664�0000000�0000000�00000015740�12670356754�0021144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAV; class XMLUtilTest extends \PHPUnit_Framework_TestCase { function testToClarkNotation() { $dom = new \DOMDocument(); $dom->loadXML('<?xml version="1.0"?><test1 xmlns="http://www.example.org/">Testdoc</test1>'); $this->assertEquals( '{http://www.example.org/}test1', XMLUtil::toClarkNotation($dom->firstChild) ); } function testToClarkNotation2() { $dom = new \DOMDocument(); $dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="http://www.example.org/">Testdoc</s:test1>'); $this->assertEquals( '{http://www.example.org/}test1', XMLUtil::toClarkNotation($dom->firstChild) ); } function testToClarkNotationDAVNamespace() { $dom = new \DOMDocument(); $dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="urn:DAV">Testdoc</s:test1>'); $this->assertEquals( '{DAV:}test1', XMLUtil::toClarkNotation($dom->firstChild) ); } function testToClarkNotationNoElem() { $dom = new \DOMDocument(); $dom->loadXML('<?xml version="1.0"?><s:test1 xmlns:s="urn:DAV">Testdoc</s:test1>'); $this->assertNull( XMLUtil::toClarkNotation($dom->firstChild->firstChild) ); } function testConvertDAVNamespace() { $xml='<?xml version="1.0"?><document xmlns="DAV:">blablabla</document>'; $this->assertEquals( '<?xml version="1.0"?><document xmlns="urn:DAV">blablabla</document>', XMLUtil::convertDAVNamespace($xml) ); } function testConvertDAVNamespace2() { $xml='<?xml version="1.0"?><s:document xmlns:s="DAV:">blablabla</s:document>'; $this->assertEquals( '<?xml version="1.0"?><s:document xmlns:s="urn:DAV">blablabla</s:document>', XMLUtil::convertDAVNamespace($xml) ); } function testConvertDAVNamespace3() { $xml='<?xml version="1.0"?><s:document xmlns="http://bla" xmlns:s="DAV:" xmlns:z="http://othernamespace">blablabla</s:document>'; $this->assertEquals( '<?xml version="1.0"?><s:document xmlns="http://bla" xmlns:s="urn:DAV" xmlns:z="http://othernamespace">blablabla</s:document>', XMLUtil::convertDAVNamespace($xml) ); } function testConvertDAVNamespace4() { $xml='<?xml version="1.0"?><document xmlns=\'DAV:\'>blablabla</document>'; $this->assertEquals( '<?xml version="1.0"?><document xmlns=\'urn:DAV\'>blablabla</document>', XMLUtil::convertDAVNamespace($xml) ); } function testConvertDAVNamespaceMixedQuotes() { $xml='<?xml version="1.0"?><document xmlns=\'DAV:" xmlns="Another attribute\'>blablabla</document>'; $this->assertEquals( $xml, XMLUtil::convertDAVNamespace($xml) ); } /** * @depends testConvertDAVNamespace */ function testLoadDOMDocument() { $xml='<?xml version="1.0"?><document></document>'; $dom = XMLUtil::loadDOMDocument($xml); $this->assertTrue($dom instanceof \DOMDocument); } /** * @depends testLoadDOMDocument * @expectedException Sabre\DAV\Exception\BadRequest */ function testLoadDOMDocumentEmpty() { XMLUtil::loadDOMDocument(''); } /** * @expectedException Sabre\DAV\Exception\BadRequest * @depends testConvertDAVNamespace */ function testLoadDOMDocumentInvalid() { $xml='<?xml version="1.0"?><document></docu'; $dom = XMLUtil::loadDOMDocument($xml); } /** * @depends testLoadDOMDocument */ function testLoadDOMDocumentUTF16() { $xml='<?xml version="1.0" encoding="UTF-16"?><root xmlns="DAV:">blabla</root>'; $xml = iconv('UTF-8','UTF-16LE',$xml); $dom = XMLUtil::loadDOMDocument($xml); $this->assertEquals('blabla',$dom->firstChild->nodeValue); } function testParseProperties() { $xml='<?xml version="1.0"?> <root xmlns="DAV:"> <prop> <displayname>Calendars</displayname> </prop> </root>'; $dom = XMLUtil::loadDOMDocument($xml); $properties = XMLUtil::parseProperties($dom->firstChild); $this->assertEquals(array( '{DAV:}displayname' => 'Calendars', ), $properties); } /** * @depends testParseProperties */ function testParsePropertiesEmpty() { $xml='<?xml version="1.0"?> <root xmlns="DAV:" xmlns:s="http://www.rooftopsolutions.nl/example"> <prop> <displayname>Calendars</displayname> </prop> <prop> <s:example /> </prop> </root>'; $dom = XMLUtil::loadDOMDocument($xml); $properties = XMLUtil::parseProperties($dom->firstChild); $this->assertEquals(array( '{DAV:}displayname' => 'Calendars', '{http://www.rooftopsolutions.nl/example}example' => null ), $properties); } /** * @depends testParseProperties */ function testParsePropertiesComplex() { $xml='<?xml version="1.0"?> <root xmlns="DAV:"> <prop> <displayname>Calendars</displayname> </prop> <prop> <someprop>Complex value <b>right here</b></someprop> </prop> </root>'; $dom = XMLUtil::loadDOMDocument($xml); $properties = XMLUtil::parseProperties($dom->firstChild); $this->assertEquals(array( '{DAV:}displayname' => 'Calendars', '{DAV:}someprop' => 'Complex value right here', ), $properties); } /** * @depends testParseProperties */ function testParsePropertiesNoProperties() { $xml='<?xml version="1.0"?> <root xmlns="DAV:"> <prop> </prop> </root>'; $dom = XMLUtil::loadDOMDocument($xml); $properties = XMLUtil::parseProperties($dom->firstChild); $this->assertEquals(array(), $properties); } function testParsePropertiesMapHref() { $xml='<?xml version="1.0"?> <root xmlns="DAV:"> <prop> <displayname>Calendars</displayname> </prop> <prop> <someprop><href>http://sabredav.org/</href></someprop> </prop> </root>'; $dom = XMLUtil::loadDOMDocument($xml); $properties = XMLUtil::parseProperties($dom->firstChild,array('{DAV:}someprop'=>'Sabre\\DAV\\Property\\Href')); $this->assertEquals(array( '{DAV:}displayname' => 'Calendars', '{DAV:}someprop' => new Property\Href('http://sabredav.org/',false), ), $properties); } function testParseClarkNotation() { $this->assertEquals(array( 'DAV:', 'foo', ), XMLUtil::parseClarkNotation('{DAV:}foo')); $this->assertEquals(array( 'http://example.org/ns/bla', 'bar-soap', ), XMLUtil::parseClarkNotation('{http://example.org/ns/bla}bar-soap')); } /** * @expectedException InvalidArgumentException */ function testParseClarkNotationFail() { XMLUtil::parseClarkNotation('}foo'); } } ��������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/����������������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0016606�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/ACLMethodTest.php�����������������������������������������������0000664�0000000�0000000�00000021701�12670356754�0021720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; class ACLMethodTest extends \PHPUnit_Framework_TestCase { /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testCallback() { $acl = new Plugin(); $server = new DAV\Server(); $server->addPlugin($acl); $acl->httpAcl($server->httpRequest, $server->httpResponse); } /** /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ function testNotSupportedByNode() { $tree = array( new DAV\SimpleCollection('test'), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request(); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $acl->httpACL($server->httpRequest, $server->httpResponse); } function testSuccessSimple() { $tree = array( new MockACLNode('test',array()), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request(); $server->httpRequest->setUrl('/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $this->assertFalse($acl->httpACL($server->httpRequest, $server->httpResponse)); } /** * @expectedException Sabre\DAVACL\Exception\NotRecognizedPrincipal */ function testUnrecognizedPrincipal() { $tree = array( new MockACLNode('test',array()), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request('ACL','/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> <d:ace> <d:grant><d:privilege><d:read /></d:privilege></d:grant> <d:principal><d:href>/principals/notfound</d:href></d:principal> </d:ace> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $acl->httpACL($server->httpRequest, $server->httpResponse); } /** * @expectedException Sabre\DAVACL\Exception\NotRecognizedPrincipal */ function testUnrecognizedPrincipal2() { $tree = array( new MockACLNode('test',array()), new DAV\SimpleCollection('principals',array( new DAV\SimpleCollection('notaprincipal'), )), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request('ACL','/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> <d:ace> <d:grant><d:privilege><d:read /></d:privilege></d:grant> <d:principal><d:href>/principals/notaprincipal</d:href></d:principal> </d:ace> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $acl->httpACL($server->httpRequest, $server->httpResponse); } /** * @expectedException Sabre\DAVACL\Exception\NotSupportedPrivilege */ function testUnknownPrivilege() { $tree = array( new MockACLNode('test',array()), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request('ACL','/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> <d:ace> <d:grant><d:privilege><d:bananas /></d:privilege></d:grant> <d:principal><d:href>/principals/notfound</d:href></d:principal> </d:ace> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $acl->httpACL($server->httpRequest, $server->httpResponse); } /** * @expectedException Sabre\DAVACL\Exception\NoAbstract */ function testAbstractPrivilege() { $tree = array( new MockACLNode('test',array()), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request('ACL','/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> <d:ace> <d:grant><d:privilege><d:all /></d:privilege></d:grant> <d:principal><d:href>/principals/notfound</d:href></d:principal> </d:ace> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $acl->httpACL($server->httpRequest, $server->httpResponse); } /** * @expectedException Sabre\DAVACL\Exception\AceConflict */ function testUpdateProtectedPrivilege() { $oldACL = array( array( 'principal' => 'principals/notfound', 'privilege' => '{DAV:}write', 'protected' => true, ), ); $tree = array( new MockACLNode('test',$oldACL), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request('ACL','/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> <d:ace> <d:grant><d:privilege><d:read /></d:privilege></d:grant> <d:principal><d:href>/principals/notfound</d:href></d:principal> </d:ace> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $acl->httpACL($server->httpRequest, $server->httpResponse); } /** * @expectedException Sabre\DAVACL\Exception\AceConflict */ function testUpdateProtectedPrivilege2() { $oldACL = array( array( 'principal' => 'principals/notfound', 'privilege' => '{DAV:}write', 'protected' => true, ), ); $tree = array( new MockACLNode('test',$oldACL), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request('ACL','/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> <d:ace> <d:grant><d:privilege><d:write /></d:privilege></d:grant> <d:principal><d:href>/principals/foo</d:href></d:principal> </d:ace> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $acl->httpACL($server->httpRequest, $server->httpResponse); } /** * @expectedException Sabre\DAVACL\Exception\AceConflict */ function testUpdateProtectedPrivilege3() { $oldACL = array( array( 'principal' => 'principals/notfound', 'privilege' => '{DAV:}write', 'protected' => true, ), ); $tree = array( new MockACLNode('test',$oldACL), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request('ACL','/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> <d:ace> <d:grant><d:privilege><d:write /></d:privilege></d:grant> <d:principal><d:href>/principals/notfound</d:href></d:principal> </d:ace> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $acl->httpACL($server->httpRequest, $server->httpResponse); } function testSuccessComplex() { $oldACL = array( array( 'principal' => 'principals/foo', 'privilege' => '{DAV:}write', 'protected' => true, ), array( 'principal' => 'principals/bar', 'privilege' => '{DAV:}read', ), ); $tree = array( $node = new MockACLNode('test',$oldACL), new DAV\SimpleCollection('principals', array( new MockPrincipal('foo','principals/foo'), new MockPrincipal('baz','principals/baz'), )), ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->httpRequest = new HTTP\Request('ACL','/test'); $body = '<?xml version="1.0"?> <d:acl xmlns:d="DAV:"> <d:ace> <d:grant><d:privilege><d:write /></d:privilege></d:grant> <d:principal><d:href>/principals/foo</d:href></d:principal> <d:protected /> </d:ace> <d:ace> <d:grant><d:privilege><d:write /></d:privilege></d:grant> <d:principal><d:href>/principals/baz</d:href></d:principal> </d:ace> </d:acl>'; $server->httpRequest->setBody($body); $server->addPlugin($acl); $this->assertFalse($acl->httpAcl($server->httpRequest, $server->httpResponse)); $this->assertEquals(array( array( 'principal' => 'principals/foo', 'privilege' => '{DAV:}write', 'protected' => true, ), array( 'principal' => 'principals/baz', 'privilege' => '{DAV:}write', 'protected' => false, ), ), $node->getACL()); } } ���������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/AllowAccessTest.php���������������������������������������������0000664�0000000�0000000�00000006572�12670356754�0022371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; class AllowAccessTest extends \PHPUnit_Framework_TestCase { /** * @var DAV\Server */ protected $server; function setUp() { $nodes = array( new DAV\SimpleCollection('testdir'), ); $this->server = new DAV\Server($nodes); $aclPlugin = new Plugin(); $aclPlugin->allowAccessToNodesWithoutACL = true; $this->server->addPlugin($aclPlugin); } function testGet() { $this->server->httpRequest->setMethod('GET'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testGetDoesntExist() { $this->server->httpRequest->setMethod('GET'); $this->server->httpRequest->setUrl('/foo'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testHEAD() { $this->server->httpRequest->setMethod('HEAD'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testOPTIONS() { $this->server->httpRequest->setMethod('OPTIONS'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testPUT() { $this->server->httpRequest->setMethod('PUT'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testACL() { $this->server->httpRequest->setMethod('ACL'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testPROPPATCH() { $this->server->httpRequest->setMethod('PROPPATCH'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testCOPY() { $this->server->httpRequest->setMethod('COPY'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testMOVE() { $this->server->httpRequest->setMethod('MOVE'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testLOCK() { $this->server->httpRequest->setMethod('LOCK'); $this->server->httpRequest->setUrl('/testdir'); $this->assertTrue($this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse])); } function testBeforeBind() { $this->assertTrue($this->server->emit('beforeBind', ['testdir/file'])); } function testBeforeUnbind() { $this->assertTrue($this->server->emit('beforeUnbind', ['testdir'])); } } ��������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/BlockAccessTest.php���������������������������������������������0000664�0000000�0000000�00000012274�12670356754�0022341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; class BlockAccessTest extends \PHPUnit_Framework_TestCase { /** * @var DAV\Server */ protected $server; protected $plugin; function setUp() { $nodes = [ new DAV\SimpleCollection('testdir'), ]; $this->server = new DAV\Server($nodes); $this->plugin = new Plugin(); $this->plugin->allowAccessToNodesWithoutACL = false; $this->server->addPlugin($this->plugin); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testGet() { $this->server->httpRequest->setMethod('GET'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } function testGetDoesntExist() { $this->server->httpRequest->setMethod('GET'); $this->server->httpRequest->setUrl('/foo'); $r = $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); $this->assertTrue($r); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testHEAD() { $this->server->httpRequest->setMethod('HEAD'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testOPTIONS() { $this->server->httpRequest->setMethod('OPTIONS'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testPUT() { $this->server->httpRequest->setMethod('PUT'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testPROPPATCH() { $this->server->httpRequest->setMethod('PROPPATCH'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testCOPY() { $this->server->httpRequest->setMethod('COPY'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testMOVE() { $this->server->httpRequest->setMethod('MOVE'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testACL() { $this->server->httpRequest->setMethod('ACL'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testLOCK() { $this->server->httpRequest->setMethod('LOCK'); $this->server->httpRequest->setUrl('/testdir'); $this->server->emit('beforeMethod', [$this->server->httpRequest, $this->server->httpResponse]); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testBeforeBind() { $this->server->emit('beforeBind', ['testdir/file']); } /** * @expectedException Sabre\DAVACL\Exception\NeedPrivileges */ function testBeforeUnbind() { $this->server->emit('beforeUnbind', ['testdir']); } function testPropFind() { $propFind = new DAV\PropFind('testdir', [ '{DAV:}displayname', '{DAV:}getcontentlength', '{DAV:}bar', '{DAV:}owner', ]); $r = $this->server->emit('propFind', [$propFind, new DAV\SimpleCollection('testdir')]); $this->assertTrue($r); $expected = [ 200 => [], 404 => [], 403 => [ '{DAV:}displayname' => null, '{DAV:}getcontentlength' => null, '{DAV:}bar' => null, '{DAV:}owner' => null, ], ]; $this->assertEquals($expected, $propFind->getResultForMultiStatus()); } function testBeforeGetPropertiesNoListing() { $this->plugin->hideNodesFromListings = true; $propFind = new DAV\PropFind('testdir', [ '{DAV:}displayname', '{DAV:}getcontentlength', '{DAV:}bar', '{DAV:}owner', ]); $r = $this->server->emit('propFind', [$propFind, new DAV\SimpleCollection('testdir')]); $this->assertFalse($r); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Exception/������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0020544�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Exception/AceConflictTest.php�����������������������������������0000664�0000000�0000000�00000001765�12670356754�0024300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Exception; use Sabre\DAV; class AceConflictTest extends \PHPUnit_Framework_TestCase { function testSerialize() { $ex = new AceConflict('message'); $server = new DAV\Server(); $dom = new \DOMDocument('1.0','utf-8'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $ex->serialize($server, $root); $xpaths = array( '/d:root' => 1, '/d:root/d:no-ace-conflict' => 1, ); // Reloading because PHP DOM sucks $dom2 = new \DOMDocument('1.0', 'utf-8'); $dom2->loadXML($dom->saveXML()); $dxpath = new \DOMXPath($dom2); $dxpath->registerNamespace('d','DAV:'); foreach($xpaths as $xpath=>$count) { $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); } } } �����������sabre-dav-2.1.10/tests/Sabre/DAVACL/Exception/NeedPrivilegesExceptionTest.php�����������������������0000664�0000000�0000000�00000002715�12670356754�0026706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Exception; use Sabre\DAV; class NeedPrivilegesTest extends \PHPUnit_Framework_TestCase { function testSerialize() { $uri = 'foo'; $privileges = array( '{DAV:}read', '{DAV:}write', ); $ex = new NeedPrivileges($uri, $privileges); $server = new DAV\Server(); $dom = new \DOMDocument('1.0','utf-8'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $ex->serialize($server, $root); $xpaths = array( '/d:root' => 1, '/d:root/d:need-privileges' => 1, '/d:root/d:need-privileges/d:resource' => 2, '/d:root/d:need-privileges/d:resource/d:href' => 2, '/d:root/d:need-privileges/d:resource/d:privilege' => 2, '/d:root/d:need-privileges/d:resource/d:privilege/d:read' => 1, '/d:root/d:need-privileges/d:resource/d:privilege/d:write' => 1, ); // Reloading because PHP DOM sucks $dom2 = new \DOMDocument('1.0', 'utf-8'); $dom2->loadXML($dom->saveXML()); $dxpath = new \DOMXPath($dom2); $dxpath->registerNamespace('d','DAV:'); foreach($xpaths as $xpath=>$count) { $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); } } } ���������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Exception/NoAbstractTest.php������������������������������������0000664�0000000�0000000�00000001757�12670356754�0024167�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Exception; use Sabre\DAV; class NoAbstractTest extends \PHPUnit_Framework_TestCase { function testSerialize() { $ex = new NoAbstract('message'); $server = new DAV\Server(); $dom = new \DOMDocument('1.0','utf-8'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $ex->serialize($server, $root); $xpaths = array( '/d:root' => 1, '/d:root/d:no-abstract' => 1, ); // Reloading because PHP DOM sucks $dom2 = new \DOMDocument('1.0', 'utf-8'); $dom2->loadXML($dom->saveXML()); $dxpath = new \DOMXPath($dom2); $dxpath->registerNamespace('d','DAV:'); foreach($xpaths as $xpath=>$count) { $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); } } } �����������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Exception/NotRecognizedPrincipalTest.php������������������������0000664�0000000�0000000�00000002020�12670356754�0026523�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Exception; use Sabre\DAV; class NotRecognizedPrincipalTest extends \PHPUnit_Framework_TestCase { function testSerialize() { $ex = new NotRecognizedPrincipal('message'); $server = new DAV\Server(); $dom = new \DOMDocument('1.0','utf-8'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $ex->serialize($server, $root); $xpaths = array( '/d:root' => 1, '/d:root/d:recognized-principal' => 1, ); // Reloading because PHP DOM sucks $dom2 = new \DOMDocument('1.0', 'utf-8'); $dom2->loadXML($dom->saveXML()); $dxpath = new \DOMXPath($dom2); $dxpath->registerNamespace('d','DAV:'); foreach($xpaths as $xpath=>$count) { $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Exception/NotSupportedPrivilegeTest.php�������������������������0000664�0000000�0000000�00000002021�12670356754�0026425�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Exception; use Sabre\DAV; class NotSupportedPrivilegeTest extends \PHPUnit_Framework_TestCase { function testSerialize() { $ex = new NotSupportedPrivilege('message'); $server = new DAV\Server(); $dom = new \DOMDocument('1.0','utf-8'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $ex->serialize($server, $root); $xpaths = array( '/d:root' => 1, '/d:root/d:not-supported-privilege' => 1, ); // Reloading because PHP DOM sucks $dom2 = new \DOMDocument('1.0', 'utf-8'); $dom2->loadXML($dom->saveXML()); $dxpath = new \DOMXPath($dom2); $dxpath->registerNamespace('d','DAV:'); foreach($xpaths as $xpath=>$count) { $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/ExpandPropertiesTest.php����������������������������������������0000664�0000000�0000000�00000027005�12670356754�0023457�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; class ExpandPropertiesTest extends \PHPUnit_Framework_TestCase { function getServer() { $tree = array( new DAV\Mock\PropertiesCollection('node1', [], array( '{http://sabredav.org/ns}simple' => 'foo', '{http://sabredav.org/ns}href' => new DAV\Property\Href('node2'), '{DAV:}displayname' => 'Node 1', )), new DAV\Mock\PropertiesCollection('node2', [], array( '{http://sabredav.org/ns}simple' => 'simple', '{http://sabredav.org/ns}hreflist' => new DAV\Property\HrefList(array('node1','node3')), '{DAV:}displayname' => 'Node 2', )), new DAV\Mock\PropertiesCollection('node3', [], array( '{http://sabredav.org/ns}simple' => 'simple', '{DAV:}displayname' => 'Node 3', )), ); $fakeServer = new DAV\Server($tree); $fakeServer->sapi = new HTTP\SapiMock(); $fakeServer->debugExceptions = true; $fakeServer->httpResponse = new HTTP\ResponseMock(); $plugin = new Plugin(); $plugin->allowAccessToNodesWithoutACL = true; $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; } function testSimple() { $xml = '<?xml version="1.0"?> <d:expand-property xmlns:d="DAV:"> <d:property name="displayname" /> <d:property name="foo" namespace="http://www.sabredav.org/NS/2010/nonexistant" /> <d:property name="simple" namespace="http://sabredav.org/ns" /> <d:property name="href" namespace="http://sabredav.org/ns" /> </d:expand-property>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/node1', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status,'Incorrect status code received. Full body: ' . $server->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $server->httpResponse->getHeaders()); $check = array( '/d:multistatus', '/d:multistatus/d:response' => 1, '/d:multistatus/d:response/d:href' => 1, '/d:multistatus/d:response/d:propstat' => 2, '/d:multistatus/d:response/d:propstat/d:prop' => 2, '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:simple' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:href' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:href' => 1, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); $xml->registerXPathNamespace('s','http://sabredav.org/ns'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response: ' . $server->httpResponse->body); } } /** * @depends testSimple */ function testExpand() { $xml = '<?xml version="1.0"?> <d:expand-property xmlns:d="DAV:"> <d:property name="href" namespace="http://sabredav.org/ns"> <d:property name="displayname" /> </d:property> </d:expand-property>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/node1', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status, 'Incorrect response status received. Full response body: ' . $server->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $server->httpResponse->getHeaders()); $check = array( '/d:multistatus', '/d:multistatus/d:response' => 1, '/d:multistatus/d:response/d:href' => 1, '/d:multistatus/d:response/d:propstat' => 1, '/d:multistatus/d:response/d:propstat/d:prop' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:href' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:href' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop/d:displayname' => 1, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); $xml->registerXPathNamespace('s','http://sabredav.org/ns'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result)); } } /** * @depends testSimple */ function testExpandHrefList() { $xml = '<?xml version="1.0"?> <d:expand-property xmlns:d="DAV:"> <d:property name="hreflist" namespace="http://sabredav.org/ns"> <d:property name="displayname" /> </d:property> </d:expand-property>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/node2', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $server->httpResponse->getHeaders()); $check = array( '/d:multistatus', '/d:multistatus/d:response' => 1, '/d:multistatus/d:response/d:href' => 1, '/d:multistatus/d:response/d:propstat' => 1, '/d:multistatus/d:response/d:propstat/d:prop' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response' => 2, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:href' => 2, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat' => 2, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop' => 2, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/d:displayname' => 2, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); $xml->registerXPathNamespace('s','http://sabredav.org/ns'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result)); } } /** * @depends testExpand */ function testExpandDeep() { $xml = '<?xml version="1.0"?> <d:expand-property xmlns:d="DAV:"> <d:property name="hreflist" namespace="http://sabredav.org/ns"> <d:property name="href" namespace="http://sabredav.org/ns"> <d:property name="displayname" /> </d:property> <d:property name="displayname" /> </d:property> </d:expand-property>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/node2', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $server->httpResponse->getHeaders()); $check = array( '/d:multistatus', '/d:multistatus/d:response' => 1, '/d:multistatus/d:response/d:href' => 1, '/d:multistatus/d:response/d:propstat' => 1, '/d:multistatus/d:response/d:propstat/d:prop' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response' => 2, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:href' => 2, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat' => 3, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop' => 3, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/d:displayname' => 2, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href' => 2, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:href' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop' => 1, '/d:multistatus/d:response/d:propstat/d:prop/s:hreflist/d:response/d:propstat/d:prop/s:href/d:response/d:propstat/d:prop/d:displayname' => 1, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); $xml->registerXPathNamespace('s','http://sabredav.org/ns'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result)); } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/MockACLNode.php�������������������������������������������������0000664�0000000�0000000�00000001215�12670356754�0021335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; class MockACLNode extends DAV\Node implements IACL { public $name; public $acl; function __construct($name, array $acl = array()) { $this->name = $name; $this->acl = $acl; } function getName() { return $this->name; } function getOwner() { return null; } function getGroup() { return null; } function getACL() { return $this->acl; } function setACL(array $acl) { $this->acl = $acl; } function getSupportedPrivilegeSet() { return null; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/MockPrincipal.php�����������������������������������������������0000664�0000000�0000000�00000002126�12670356754�0022053�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; class MockPrincipal extends DAV\Node implements IPrincipal { public $name; public $principalUrl; public $groupMembership = array(); public $groupMemberSet = array(); function __construct($name,$principalUrl,array $groupMembership = array(), array $groupMemberSet = array()) { $this->name = $name; $this->principalUrl = $principalUrl; $this->groupMembership = $groupMembership; $this->groupMemberSet = $groupMemberSet; } function getName() { return $this->name; } function getDisplayName() { return $this->getName(); } function getAlternateUriSet() { return array(); } function getPrincipalUrl() { return $this->principalUrl; } function getGroupMemberSet() { return $this->groupMemberSet; } function getGroupMemberShip() { return $this->groupMembership; } function setGroupMemberSet(array $groupMemberSet) { $this->groupMemberSet = $groupMemberSet; } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PluginAdminTest.php���������������������������������������������0000664�0000000�0000000�00000004266�12670356754�0022376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAVACL/MockACLNode.php'; require_once 'Sabre/HTTP/ResponseMock.php'; class PluginAdminTest extends \PHPUnit_Framework_TestCase { function testNoAdminAccess() { $principalBackend = new PrincipalBackend\Mock(); $tree = array( new MockACLNode('adminonly', array()), new PrincipalCollection($principalBackend), ); $fakeServer = new DAV\Server($tree); $fakeServer->sapi = new HTTP\SapiMock(); $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm'); $fakeServer->addPlugin($plugin); $plugin = new Plugin(); $fakeServer->addPlugin($plugin); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'OPTIONS', 'HTTP_DEPTH' => 1, 'REQUEST_URI' => '/adminonly', )); $response = new HTTP\ResponseMock(); $fakeServer->httpRequest = $request; $fakeServer->httpResponse = $response; $fakeServer->exec(); $this->assertEquals(403, $response->status); } /** * @depends testNoAdminAccess */ function testAdminAccess() { $principalBackend = new PrincipalBackend\Mock(); $tree = array( new MockACLNode('adminonly', array()), new PrincipalCollection($principalBackend), ); $fakeServer = new DAV\Server($tree); $fakeServer->sapi = new HTTP\SapiMock(); $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm'); $fakeServer->addPlugin($plugin); $plugin = new Plugin(); $plugin->adminPrincipals = array( 'principals/admin', ); $fakeServer->addPlugin($plugin); $request = HTTP\Sapi::createFromServerArray(array( 'REQUEST_METHOD' => 'OPTIONS', 'HTTP_DEPTH' => 1, 'REQUEST_URI' => '/adminonly', )); $response = new HTTP\ResponseMock(); $fakeServer->httpRequest = $request; $fakeServer->httpResponse = $response; $fakeServer->exec(); $this->assertEquals(200, $response->status); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PluginPropertiesTest.php����������������������������������������0000664�0000000�0000000�00000031303�12670356754�0023472�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; class PluginPropertiesTest extends \PHPUnit_Framework_TestCase { function testPrincipalCollectionSet() { $plugin = new Plugin(); $plugin->principalCollectionSet = [ 'principals1', 'principals2', ]; $requestedProperties = [ '{DAV:}principal-collection-set', ]; $server = new DAV\Server(new DAV\SimpleCollection('root')); $server->addPlugin($plugin); $result = $server->getPropertiesForPath('', $requestedProperties); $result = $result[0]; $this->assertEquals(1,count($result[200])); $this->assertArrayHasKey('{DAV:}principal-collection-set',$result[200]); $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $result[200]['{DAV:}principal-collection-set']); $expected = [ 'principals1/', 'principals2/', ]; $this->assertEquals($expected, $result[200]['{DAV:}principal-collection-set']->getHrefs()); } function testCurrentUserPrincipal() { $fakeServer = new DAV\Server(); $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm'); $fakeServer->addPlugin($plugin); $plugin = new Plugin(); $fakeServer->addPlugin($plugin); $requestedProperties = [ '{DAV:}current-user-principal', ]; $result = $fakeServer->getPropertiesForPath('', $requestedProperties); $result = $result[0]; $this->assertEquals(1,count($result[200])); $this->assertArrayHasKey('{DAV:}current-user-principal',$result[200]); $this->assertInstanceOf('Sabre\DAVACL\Property\Principal', $result[200]['{DAV:}current-user-principal']); $this->assertEquals(Property\Principal::UNAUTHENTICATED, $result[200]['{DAV:}current-user-principal']->getType()); // This will force the login $fakeServer->emit('beforeMethod', [$fakeServer->httpRequest, $fakeServer->httpResponse]); $result = $fakeServer->getPropertiesForPath('', $requestedProperties); $result = $result[0]; $this->assertEquals(1,count($result[200])); $this->assertArrayHasKey('{DAV:}current-user-principal',$result[200]); $this->assertInstanceOf('Sabre\DAVACL\Property\Principal', $result[200]['{DAV:}current-user-principal']); $this->assertEquals(Property\Principal::HREF, $result[200]['{DAV:}current-user-principal']->getType()); $this->assertEquals('principals/admin/', $result[200]['{DAV:}current-user-principal']->getHref()); } function testSupportedPrivilegeSet() { $plugin = new Plugin(); $server = new DAV\Server(); $server->addPlugin($plugin); $requestedProperties = [ '{DAV:}supported-privilege-set', ]; $result = $server->getPropertiesForPath('', $requestedProperties); $result = $result[0]; $this->assertEquals(1,count($result[200])); $this->assertArrayHasKey('{DAV:}supported-privilege-set',$result[200]); $this->assertInstanceOf('Sabre\\DAVACL\\Property\\SupportedPrivilegeSet', $result[200]['{DAV:}supported-privilege-set']); $server = new DAV\Server(); $prop = $result[200]['{DAV:}supported-privilege-set']; $dom = new \DOMDocument('1.0', 'utf-8'); $root = $dom->createElement('d:root'); $root->setAttribute('xmlns:d','DAV:'); $dom->appendChild($root); $prop->serialize($server, $root); $xpaths = [ '/d:root' => 1, '/d:root/d:supported-privilege' => 1, '/d:root/d:supported-privilege/d:privilege' => 1, '/d:root/d:supported-privilege/d:privilege/d:all' => 1, '/d:root/d:supported-privilege/d:abstract' => 1, '/d:root/d:supported-privilege/d:supported-privilege' => 2, '/d:root/d:supported-privilege/d:supported-privilege/d:privilege' => 2, '/d:root/d:supported-privilege/d:supported-privilege/d:privilege/d:read' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:privilege/d:write' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege' => 8, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege' => 8, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:read-acl' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:read-current-user-privilege-set' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:write-content' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:write-properties' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:write-acl' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:bind' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:unbind' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:privilege/d:unlock' => 1, '/d:root/d:supported-privilege/d:supported-privilege/d:supported-privilege/d:abstract' => 0, ]; // reloading because php dom sucks $dom2 = new \DOMDocument('1.0', 'utf-8'); $dom2->loadXML($dom->saveXML()); $dxpath = new \DOMXPath($dom2); $dxpath->registerNamespace('d','DAV:'); foreach($xpaths as $xpath=>$count) { $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); } } function testACL() { $plugin = new Plugin(); $nodes = [ new MockACLNode('foo', [ [ 'principal' => 'principals/admin', 'privilege' => '{DAV:}read', ] ]), new DAV\SimpleCollection('principals', [ $principal = new MockPrincipal('admin','principals/admin'), ]), ]; $server = new DAV\Server($nodes); $server->addPlugin($plugin); $authPlugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm'); $server->addPlugin($authPlugin); // Force login $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); $requestedProperties = [ '{DAV:}acl', ]; $result = $server->getPropertiesForPath('foo', $requestedProperties); $result = $result[0]; $this->assertEquals(1,count($result[200]),'The {DAV:}acl property did not return from the list. Full list: ' . print_r($result, true)); $this->assertArrayHasKey('{DAV:}acl',$result[200]); $this->assertInstanceOf('Sabre\\DAVACL\\Property\\ACL', $result[200]['{DAV:}acl']); } function testACLRestrictions() { $plugin = new Plugin(); $nodes = [ new MockACLNode('foo', [ [ 'principal' => 'principals/admin', 'privilege' => '{DAV:}read', ] ]), new DAV\SimpleCollection('principals', [ $principal = new MockPrincipal('admin','principals/admin'), ]), ]; $server = new DAV\Server($nodes); $server->addPlugin($plugin); $authPlugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'realm'); $server->addPlugin($authPlugin); // Force login $authPlugin->beforeMethod(new HTTP\Request(), new HTTP\Response()); $requestedProperties = [ '{DAV:}acl-restrictions', ]; $result = $server->getPropertiesForPath('foo', $requestedProperties); $result = $result[0]; $this->assertEquals(1,count($result[200]),'The {DAV:}acl-restrictions property did not return from the list. Full list: ' . print_r($result, true)); $this->assertArrayHasKey('{DAV:}acl-restrictions',$result[200]); $this->assertInstanceOf('Sabre\\DAVACL\\Property\\ACLRestrictions', $result[200]['{DAV:}acl-restrictions']); } function testAlternateUriSet() { $tree = [ new DAV\SimpleCollection('principals', [ $principal = new MockPrincipal('user','principals/user'), ]) ]; $fakeServer = new DAV\Server($tree); //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend(),'realm'); //$fakeServer->addPlugin($plugin); $plugin = new Plugin(); $fakeServer->addPlugin($plugin); $requestedProperties = [ '{DAV:}alternate-URI-set', ]; $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); $result = $result[0]; $this->assertTrue(isset($result[200])); $this->assertTrue(isset($result[200]['{DAV:}alternate-URI-set'])); $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $result[200]['{DAV:}alternate-URI-set']); $this->assertEquals([], $result[200]['{DAV:}alternate-URI-set']->getHrefs()); } function testPrincipalURL() { $tree = [ new DAV\SimpleCollection('principals', [ $principal = new MockPrincipal('user','principals/user'), ]), ]; $fakeServer = new DAV\Server($tree); //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend(),'realm'); //$fakeServer->addPlugin($plugin); $plugin = new Plugin(); $fakeServer->addPlugin($plugin); $requestedProperties = [ '{DAV:}principal-URL', ]; $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); $result = $result[0]; $this->assertTrue(isset($result[200])); $this->assertTrue(isset($result[200]['{DAV:}principal-URL'])); $this->assertInstanceOf('Sabre\\DAV\\Property\\Href', $result[200]['{DAV:}principal-URL']); $this->assertEquals('principals/user/', $result[200]['{DAV:}principal-URL']->getHref()); } function testGroupMemberSet() { $tree = [ new DAV\SimpleCollection('principals', [ $principal = new MockPrincipal('user','principals/user'), ]), ]; $fakeServer = new DAV\Server($tree); //$plugin = new DAV\Auth\Plugin(new DAV\Auth\MockBackend(),'realm'); //$fakeServer->addPlugin($plugin); $plugin = new Plugin(); $fakeServer->addPlugin($plugin); $requestedProperties = [ '{DAV:}group-member-set', ]; $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); $result = $result[0]; $this->assertTrue(isset($result[200])); $this->assertTrue(isset($result[200]['{DAV:}group-member-set'])); $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $result[200]['{DAV:}group-member-set']); $this->assertEquals([], $result[200]['{DAV:}group-member-set']->getHrefs()); } function testGroupMemberShip() { $tree = [ new DAV\SimpleCollection('principals', [ $principal = new MockPrincipal('user','principals/user'), ]), ]; $fakeServer = new DAV\Server($tree); $plugin = new Plugin(); $fakeServer->addPlugin($plugin); $requestedProperties = [ '{DAV:}group-membership', ]; $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); $result = $result[0]; $this->assertTrue(isset($result[200])); $this->assertTrue(isset($result[200]['{DAV:}group-membership'])); $this->assertInstanceOf('Sabre\\DAV\\Property\\HrefList', $result[200]['{DAV:}group-membership']); $this->assertEquals([], $result[200]['{DAV:}group-membership']->getHrefs()); } function testGetDisplayName() { $tree = [ new DAV\SimpleCollection('principals', [ $principal = new MockPrincipal('user','principals/user'), ]), ]; $fakeServer = new DAV\Server($tree); $plugin = new Plugin(); $fakeServer->addPlugin($plugin); $requestedProperties = [ '{DAV:}displayname', ]; $result = $fakeServer->getPropertiesForPath('principals/user', $requestedProperties); $result = $result[0]; $this->assertTrue(isset($result[200])); $this->assertTrue(isset($result[200]['{DAV:}displayname'])); $this->assertEquals('user', $result[200]['{DAV:}displayname']); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PluginUpdatePropertiesTest.php����������������������������������0000664�0000000�0000000�00000005164�12670356754�0024643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAVACL/MockPrincipal.php'; class PluginUpdatePropertiesTest extends \PHPUnit_Framework_TestCase { public function testUpdatePropertiesPassthrough() { $tree = array( new DAV\SimpleCollection('foo'), ); $server = new DAV\Server($tree); $server->addPlugin(new Plugin()); $result = $server->updateProperties('foo', array( '{DAV:}foo' => 'bar', )); $expected = array( '{DAV:}foo' => 403, ); $this->assertEquals($expected, $result); } public function testRemoveGroupMembers() { $tree = array( new MockPrincipal('foo','foo'), ); $server = new DAV\Server($tree); $server->addPlugin(new Plugin()); $result = $server->updateProperties('foo', array( '{DAV:}group-member-set' => null, )); $expected = array( '{DAV:}group-member-set' => 204 ); $this->assertEquals($expected, $result); $this->assertEquals(array(),$tree[0]->getGroupMemberSet()); } public function testSetGroupMembers() { $tree = array( new MockPrincipal('foo','foo'), ); $server = new DAV\Server($tree); $server->addPlugin(new Plugin()); $result = $server->updateProperties('foo', array( '{DAV:}group-member-set' => new DAV\Property\HrefList(array('/bar','/baz'), true), )); $expected = array( '{DAV:}group-member-set' => 200 ); $this->assertEquals($expected, $result); $this->assertEquals(array('bar','baz'),$tree[0]->getGroupMemberSet()); } /** * @expectedException Sabre\DAV\Exception */ public function testSetBadValue() { $tree = array( new MockPrincipal('foo','foo'), ); $server = new DAV\Server($tree); $server->addPlugin(new Plugin()); $result = $server->updateProperties('foo', array( '{DAV:}group-member-set' => new \StdClass(), )); } public function testSetBadNode() { $tree = array( new DAV\SimpleCollection('foo'), ); $server = new DAV\Server($tree); $server->addPlugin(new Plugin()); $result = $server->updateProperties('foo', array( '{DAV:}group-member-set' => new DAV\Property\HrefList(array('/bar','/baz'),false), )); $expected = array( '{DAV:}group-member-set' => 403, ); $this->assertEquals($expected, $result); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalBackend/�����������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0021777�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalBackend/AbstractPDOTest.php����������������������������0000664�0000000�0000000�00000012373�12670356754�0025464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\PrincipalBackend; use Sabre\DAV; use Sabre\HTTP; abstract class AbstractPDOTest extends \PHPUnit_Framework_TestCase { abstract function getPDO(); function testConstruct() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $this->assertTrue($backend instanceof PDO); } /** * @depends testConstruct */ function testGetPrincipalsByPrefix() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $expected = array( array( 'uri' => 'principals/user', '{http://sabredav.org/ns}email-address' => 'user@example.org', '{DAV:}displayname' => 'User', ), array( 'uri' => 'principals/group', '{http://sabredav.org/ns}email-address' => 'group@example.org', '{DAV:}displayname' => 'Group', ), ); $this->assertEquals($expected, $backend->getPrincipalsByPrefix('principals')); $this->assertEquals(array(), $backend->getPrincipalsByPrefix('foo')); } /** * @depends testConstruct */ function testGetPrincipalByPath() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $expected = array( 'id' => 1, 'uri' => 'principals/user', '{http://sabredav.org/ns}email-address' => 'user@example.org', '{DAV:}displayname' => 'User', ); $this->assertEquals($expected, $backend->getPrincipalByPath('principals/user')); $this->assertEquals(null, $backend->getPrincipalByPath('foo')); } function testGetGroupMemberSet() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $expected = array('principals/user'); $this->assertEquals($expected,$backend->getGroupMemberSet('principals/group')); } function testGetGroupMembership() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $expected = array('principals/group'); $this->assertEquals($expected,$backend->getGroupMembership('principals/user')); } function testSetGroupMemberSet() { $pdo = $this->getPDO(); // Start situation $backend = new PDO($pdo); $this->assertEquals(array('principals/user'), $backend->getGroupMemberSet('principals/group')); // Removing all principals $backend->setGroupMemberSet('principals/group', array()); $this->assertEquals(array(), $backend->getGroupMemberSet('principals/group')); // Adding principals again $backend->setGroupMemberSet('principals/group', array('principals/user')); $this->assertEquals(array('principals/user'), $backend->getGroupMemberSet('principals/group')); } function testSearchPrincipals() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $result = $backend->searchPrincipals('principals', array('{DAV:}blabla' => 'foo')); $this->assertEquals(array(), $result); $result = $backend->searchPrincipals('principals', array('{DAV:}displayname' => 'ou')); $this->assertEquals(array('principals/group'), $result); $result = $backend->searchPrincipals('principals', array('{DAV:}displayname' => 'UsEr', '{http://sabredav.org/ns}email-address' => 'USER@EXAMPLE')); $this->assertEquals(array('principals/user'), $result); $result = $backend->searchPrincipals('mom', array('{DAV:}displayname' => 'UsEr', '{http://sabredav.org/ns}email-address' => 'USER@EXAMPLE')); $this->assertEquals(array(), $result); } function testUpdatePrincipal() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $propPatch = new DAV\PropPatch([ '{DAV:}displayname' => 'pietje', '{http://sabredav.org/ns}vcard-url' => 'blabla', ]); $backend->updatePrincipal('principals/user', $propPatch); $result = $propPatch->commit(); $this->assertTrue($result); $this->assertEquals(array( 'id' => 1, 'uri' => 'principals/user', '{DAV:}displayname' => 'pietje', '{http://sabredav.org/ns}vcard-url' => 'blabla', '{http://sabredav.org/ns}email-address' => 'user@example.org', ), $backend->getPrincipalByPath('principals/user')); } function testUpdatePrincipalUnknownField() { $pdo = $this->getPDO(); $backend = new PDO($pdo); $propPatch = new DAV\PropPatch([ '{DAV:}displayname' => 'pietje', '{http://sabredav.org/ns}vcard-url' => 'blabla', '{DAV:}unknown' => 'foo', ]); $backend->updatePrincipal('principals/user', $propPatch); $result = $propPatch->commit(); $this->assertFalse($result); $this->assertEquals(array( '{DAV:}displayname' => 424, '{http://sabredav.org/ns}vcard-url' => 424, '{DAV:}unknown' => 403 ), $propPatch->getResult()); $this->assertEquals(array( 'id' => '1', 'uri' => 'principals/user', '{DAV:}displayname' => 'User', '{http://sabredav.org/ns}email-address' => 'user@example.org', ), $backend->getPrincipalByPath('principals/user')); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalBackend/Mock.php���������������������������������������0000664�0000000�0000000�00000010252�12670356754�0023401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\PrincipalBackend; class Mock extends AbstractBackend { public $groupMembers = array(); public $principals; function __construct() { $this->principals = array( array( 'uri' => 'principals/user1', '{DAV:}displayname' => 'User 1', '{http://sabredav.org/ns}email-address' => 'user1.sabredav@sabredav.org', '{http://sabredav.org/ns}vcard-url' => 'addressbooks/user1/book1/vcard1.vcf', ), array( 'uri' => 'principals/admin', '{DAV:}displayname' => 'Admin', ), array( 'uri' => 'principals/user2', '{DAV:}displayname' => 'User 2', '{http://sabredav.org/ns}email-address' => 'user2.sabredav@sabredav.org', ), ); } function getPrincipalsByPrefix($prefix) { $prefix = trim($prefix,'/'); if ($prefix) $prefix.='/'; $return = array(); foreach($this->principals as $principal) { if ($prefix && strpos($principal['uri'], $prefix)!==0) continue; $return[] = $principal; } return $return; } function addPrincipal(array $principal) { $this->principals[] = $principal; } function getPrincipalByPath($path) { foreach($this->getPrincipalsByPrefix('principals') as $principal) { if ($principal['uri'] === $path) return $principal; } } function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { $matches = array(); foreach($this->getPrincipalsByPrefix($prefixPath) as $principal) { foreach($searchProperties as $key=>$value) { if (!isset($principal[$key])) { continue 2; } if (mb_stripos($principal[$key],$value, 0, 'UTF-8')===false) { continue 2; } // We have a match for this searchProperty! if ($test === 'allof') { continue; } else { break; } } $matches[] = $principal['uri']; } return $matches; } function getGroupMemberSet($path) { return isset($this->groupMembers[$path]) ? $this->groupMembers[$path] : array(); } function getGroupMembership($path) { $membership = array(); foreach($this->groupMembers as $group=>$members) { if (in_array($path, $members)) $membership[] = $group; } return $membership; } function setGroupMemberSet($path, array $members) { $this->groupMembers[$path] = $members; } /** * Updates one ore more webdav properties on a principal. * * The list of mutations is stored in a Sabre\DAV\PropPatch object. * To do the actual updates, you must tell this object which properties * you're going to process with the handle() method. * * Calling the handle method is like telling the PropPatch object "I * promise I can handle updating this property". * * Read the PropPatch documenation for more info and examples. * * @param string $path * @param \Sabre\DAV\PropPatch $propPatch */ public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) { $value = null; foreach($this->principals as $principalIndex=>$value) { if ($value['uri'] === $path) { $principal = $value; break; } } if (!$principal) return; $propPatch->handleRemaining(function($mutations) use ($principal, $principalIndex) { foreach($mutations as $prop=>$value) { if (is_null($value) && isset($principal[$prop])) { unset($principal[$prop]); } else { $principal[$prop] = $value; } } $this->principals[$principalIndex] = $principal; return true; }); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalBackend/PDOMySQLTest.php�������������������������������0000664�0000000�0000000�00000002677�12670356754�0024674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\PrincipalBackend; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/TestUtil.php'; class PDOMySQLTest extends AbstractPDOTest { function getPDO() { if (!SABRE_HASMYSQL) $this->markTestSkipped('MySQL driver is not available, or not properly configured'); $pdo = \Sabre\TestUtil::getMySQLDB(); if (!$pdo) $this->markTestSkipped('Could not connect to MySQL database'); $pdo->query("DROP TABLE IF EXISTS principals"); $pdo->query(" create table principals ( id integer unsigned not null primary key auto_increment, uri varchar(50), email varchar(80), displayname VARCHAR(80), vcardurl VARCHAR(80), unique(uri) );"); $pdo->query("INSERT INTO principals (uri,email,displayname) VALUES ('principals/user','user@example.org','User')"); $pdo->query("INSERT INTO principals (uri,email,displayname) VALUES ('principals/group','group@example.org','Group')"); $pdo->query("DROP TABLE IF EXISTS groupmembers"); $pdo->query("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) );"); $pdo->query("INSERT INTO groupmembers (principal_id,member_id) VALUES (2,1)"); return $pdo; } } �����������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalBackend/PDOSqliteTest.php������������������������������0000664�0000000�0000000�00000002542�12670356754�0025157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\PrincipalBackend; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAV/Auth/Backend/AbstractPDOTest.php'; class PDOSQLiteTest extends AbstractPDOTest { function tearDown() { if (file_exists(SABRE_TEMPDIR . '/pdobackend')) unlink(SABRE_TEMPDIR . '/pdobackend'); if (file_exists(SABRE_TEMPDIR . '/pdobackend2')) unlink(SABRE_TEMPDIR . '/pdobackend2'); } function getPDO() { if (!SABRE_HASSQLITE) $this->markTestSkipped('SQLite driver is not available'); $pdo = new \PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend'); $pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); $pdo->query('CREATE TABLE principals (id INTEGER PRIMARY KEY ASC, uri TEXT, email VARCHAR(80), displayname VARCHAR(80), vcardurl VARCHAR(80))'); $pdo->query('INSERT INTO principals VALUES (1, "principals/user","user@example.org","User",null)'); $pdo->query('INSERT INTO principals VALUES (2, "principals/group","group@example.org","Group",null)'); $pdo->query("CREATE TABLE groupmembers ( id INTEGER PRIMARY KEY ASC, principal_id INT, member_id INT, UNIQUE(principal_id, member_id) );"); $pdo->query("INSERT INTO groupmembers (principal_id,member_id) VALUES (2,1)"); return $pdo; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalCollectionTest.php�������������������������������������0000664�0000000�0000000�00000002733�12670356754�0024121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; class PrincipalCollectionTest extends \PHPUnit_Framework_TestCase { public function testBasic() { $backend = new PrincipalBackend\Mock(); $pc = new PrincipalCollection($backend); $this->assertTrue($pc instanceof PrincipalCollection); $this->assertEquals('principals',$pc->getName()); } /** * @depends testBasic */ public function testGetChildren() { $backend = new PrincipalBackend\Mock(); $pc = new PrincipalCollection($backend); $children = $pc->getChildren(); $this->assertTrue(is_array($children)); foreach($children as $child) { $this->assertTrue($child instanceof IPrincipal); } } /** * @depends testBasic * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ public function testGetChildrenDisable() { $backend = new PrincipalBackend\Mock(); $pc = new PrincipalCollection($backend); $pc->disableListing = true; $children = $pc->getChildren(); } public function testFindByUri() { $backend = new PrincipalBackend\Mock(); $pc = new PrincipalCollection($backend); $this->assertEquals('principals/user1', $pc->findByUri('mailto:user1.sabredav@sabredav.org')); $this->assertNull($pc->findByUri('mailto:fake.user.sabredav@sabredav.org')); $this->assertNull($pc->findByUri('')); } } �������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalPropertySearchTest.php���������������������������������0000664�0000000�0000000�00000026426�12670356754�0025005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; class PrincipalPropertySearchTest extends \PHPUnit_Framework_TestCase { function getServer() { $backend = new PrincipalBackend\Mock(); $dir = new DAV\SimpleCollection('root'); $principals = new PrincipalCollection($backend); $dir->addChild($principals); $fakeServer = new DAV\Server($dir); $fakeServer->sapi = new HTTP\SapiMock(); $fakeServer->httpResponse = new HTTP\ResponseMock(); $fakeServer->debugExceptions = true; $plugin = new MockPlugin($backend,'realm'); $plugin->allowAccessToNodesWithoutACL = true; $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; } function testDepth1() { $xml = '<?xml version="1.0"?> <d:principal-property-search xmlns:d="DAV:"> <d:property-search> <d:prop> <d:displayname /> </d:prop> <d:match>user</d:match> </d:property-search> <d:prop> <d:displayname /> <d:getcontentlength /> </d:prop> </d:principal-property-search>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '1', 'REQUEST_URI' => '/principals', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(400, $server->httpResponse->status); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $server->httpResponse->getHeaders()); } function testUnknownSearchField() { $xml = '<?xml version="1.0"?> <d:principal-property-search xmlns:d="DAV:"> <d:property-search> <d:prop> <d:yourmom /> </d:prop> <d:match>user</d:match> </d:property-search> <d:prop> <d:displayname /> <d:getcontentlength /> </d:prop> </d:principal-property-search>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/principals', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'Vary' => ['Brief,Prefer'], ), $server->httpResponse->getHeaders()); } function testCorrect() { $xml = '<?xml version="1.0"?> <d:principal-property-search xmlns:d="DAV:"> <d:apply-to-principal-collection-set /> <d:property-search> <d:prop> <d:displayname /> </d:prop> <d:match>user</d:match> </d:property-search> <d:prop> <d:displayname /> <d:getcontentlength /> </d:prop> </d:principal-property-search>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'Vary' => ['Brief,Prefer'], ), $server->httpResponse->getHeaders()); $check = array( '/d:multistatus', '/d:multistatus/d:response' => 2, '/d:multistatus/d:response/d:href' => 2, '/d:multistatus/d:response/d:propstat' => 4, '/d:multistatus/d:response/d:propstat/d:prop' => 4, '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 2, '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 2, '/d:multistatus/d:response/d:propstat/d:status' => 4, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); } } function testAND() { $xml = '<?xml version="1.0"?> <d:principal-property-search xmlns:d="DAV:"> <d:apply-to-principal-collection-set /> <d:property-search> <d:prop> <d:displayname /> </d:prop> <d:match>user</d:match> </d:property-search> <d:property-search> <d:prop> <d:foo /> </d:prop> <d:match>bar</d:match> </d:property-search> <d:prop> <d:displayname /> <d:getcontentlength /> </d:prop> </d:principal-property-search>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'Vary' => ['Brief,Prefer'], ), $server->httpResponse->getHeaders()); $check = array( '/d:multistatus', '/d:multistatus/d:response' => 0, '/d:multistatus/d:response/d:href' => 0, '/d:multistatus/d:response/d:propstat' => 0, '/d:multistatus/d:response/d:propstat/d:prop' => 0, '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 0, '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 0, '/d:multistatus/d:response/d:propstat/d:status' => 0, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); } } function testOR() { $xml = '<?xml version="1.0"?> <d:principal-property-search xmlns:d="DAV:" test="anyof"> <d:apply-to-principal-collection-set /> <d:property-search> <d:prop> <d:displayname /> </d:prop> <d:match>user</d:match> </d:property-search> <d:property-search> <d:prop> <d:foo /> </d:prop> <d:match>bar</d:match> </d:property-search> <d:prop> <d:displayname /> <d:getcontentlength /> </d:prop> </d:principal-property-search>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'Vary' => ['Brief,Prefer'], ), $server->httpResponse->getHeaders()); $check = array( '/d:multistatus', '/d:multistatus/d:response' => 2, '/d:multistatus/d:response/d:href' => 2, '/d:multistatus/d:response/d:propstat' => 4, '/d:multistatus/d:response/d:propstat/d:prop' => 4, '/d:multistatus/d:response/d:propstat/d:prop/d:displayname' => 2, '/d:multistatus/d:response/d:propstat/d:prop/d:getcontentlength' => 2, '/d:multistatus/d:response/d:propstat/d:status' => 4, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); } } function testWrongUri() { $xml = '<?xml version="1.0"?> <d:principal-property-search xmlns:d="DAV:"> <d:property-search> <d:prop> <d:displayname /> </d:prop> <d:match>user</d:match> </d:property-search> <d:prop> <d:displayname /> <d:getcontentlength /> </d:prop> </d:principal-property-search>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(207, $server->httpResponse->status, $server->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], 'Vary' => ['Brief,Prefer'], ), $server->httpResponse->getHeaders()); $check = array( '/d:multistatus', '/d:multistatus/d:response' => 0, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); } } } class MockPlugin extends Plugin { function getCurrentUserPrivilegeSet($node) { return array( '{DAV:}read', '{DAV:}write', ); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalSearchPropertySetTest.php������������������������������0000664�0000000�0000000�00000010452�12670356754�0025451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/HTTP/ResponseMock.php'; class PrincipalSearchPropertySetTest extends \PHPUnit_Framework_TestCase { function getServer() { $backend = new PrincipalBackend\Mock(); $dir = new DAV\SimpleCollection('root'); $principals = new PrincipalCollection($backend); $dir->addChild($principals); $fakeServer = new DAV\Server($dir); $fakeServer->sapi = new HTTP\SapiMock(); $fakeServer->httpResponse = new HTTP\ResponseMock(); $plugin = new Plugin($backend,'realm'); $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; } function testDepth1() { $xml = '<?xml version="1.0"?> <d:principal-search-property-set xmlns:d="DAV:" />'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '1', 'REQUEST_URI' => '/principals', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(400, $server->httpResponse->status); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $server->httpResponse->getHeaders()); } function testDepthIncorrectXML() { $xml = '<?xml version="1.0"?> <d:principal-search-property-set xmlns:d="DAV:"><d:ohell /></d:principal-search-property-set>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/principals', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(400, $server->httpResponse->status, $server->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $server->httpResponse->getHeaders()); } function testCorrect() { $xml = '<?xml version="1.0"?> <d:principal-search-property-set xmlns:d="DAV:"/>'; $serverVars = array( 'REQUEST_METHOD' => 'REPORT', 'HTTP_DEPTH' => '0', 'REQUEST_URI' => '/principals', ); $request = HTTP\Sapi::createFromServerArray($serverVars); $request->setBody($xml); $server = $this->getServer(); $server->httpRequest = $request; $server->exec(); $this->assertEquals(200, $server->httpResponse->status, $server->httpResponse->body); $this->assertEquals(array( 'X-Sabre-Version' => [DAV\Version::VERSION], 'Content-Type' => ['application/xml; charset=utf-8'], ), $server->httpResponse->getHeaders()); $check = array( '/d:principal-search-property-set', '/d:principal-search-property-set/d:principal-search-property' => 2, '/d:principal-search-property-set/d:principal-search-property/d:prop' => 2, '/d:principal-search-property-set/d:principal-search-property/d:prop/d:displayname' => 1, '/d:principal-search-property-set/d:principal-search-property/d:prop/s:email-address' => 1, '/d:principal-search-property-set/d:principal-search-property/d:description' => 2, ); $xml = simplexml_load_string($server->httpResponse->body); $xml->registerXPathNamespace('d','DAV:'); $xml->registerXPathNamespace('s','http://sabredav.org/ns'); foreach($check as $v1=>$v2) { $xpath = is_int($v1)?$v2:$v1; $result = $xml->xpath($xpath); $count = 1; if (!is_int($v1)) $count = $v2; $this->assertEquals($count,count($result), 'we expected ' . $count . ' appearances of ' . $xpath . ' . We found ' . count($result) . '. Full response body: ' . $server->httpResponse->body); } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/PrincipalTest.php�����������������������������������������������0000664�0000000�0000000�00000014706�12670356754�0022110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; class PrincipalTest extends \PHPUnit_Framework_TestCase { public function testConstruct() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertTrue($principal instanceof Principal); } /** * @expectedException Sabre\DAV\Exception */ public function testConstructNoUri() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array()); } public function testGetName() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertEquals('admin',$principal->getName()); } public function testGetDisplayName() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertEquals('admin',$principal->getDisplayname()); $principal = new Principal($principalBackend, array( 'uri' => 'principals/admin', '{DAV:}displayname' => 'Mr. Admin' )); $this->assertEquals('Mr. Admin',$principal->getDisplayname()); } public function testGetProperties() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array( 'uri' => 'principals/admin', '{DAV:}displayname' => 'Mr. Admin', '{http://www.example.org/custom}custom' => 'Custom', '{http://sabredav.org/ns}email-address' => 'admin@example.org', )); $keys = array( '{DAV:}displayname', '{http://www.example.org/custom}custom', '{http://sabredav.org/ns}email-address', ); $props = $principal->getProperties($keys); foreach($keys as $key) $this->assertArrayHasKey($key,$props); $this->assertEquals('Mr. Admin',$props['{DAV:}displayname']); $this->assertEquals('admin@example.org', $props['{http://sabredav.org/ns}email-address']); } public function testUpdateProperties() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $propPatch = new DAV\PropPatch(array('{DAV:}yourmom' => 'test')); $result = $principal->propPatch($propPatch); $result = $propPatch->commit(); $this->assertTrue($result); } public function testGetPrincipalUrl() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertEquals('principals/admin',$principal->getPrincipalUrl()); } public function testGetAlternateUriSet() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array( 'uri' => 'principals/admin', '{DAV:}displayname' => 'Mr. Admin', '{http://www.example.org/custom}custom' => 'Custom', '{http://sabredav.org/ns}email-address' => 'admin@example.org', '{DAV:}alternate-URI-set' => array( 'mailto:admin+1@example.org', 'mailto:admin+2@example.org', 'mailto:admin@example.org', ), )); $expected = array( 'mailto:admin+1@example.org', 'mailto:admin+2@example.org', 'mailto:admin@example.org', ); $this->assertEquals($expected,$principal->getAlternateUriSet()); } public function testGetAlternateUriSetEmpty() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array( 'uri' => 'principals/admin', )); $expected = array(); $this->assertEquals($expected,$principal->getAlternateUriSet()); } public function testGetGroupMemberSet() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertEquals(array(),$principal->getGroupMemberSet()); } public function testGetGroupMembership() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertEquals(array(),$principal->getGroupMembership()); } public function testSetGroupMemberSet() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $principal->setGroupMemberSet(array('principals/foo')); $this->assertEquals(array( 'principals/admin' => array('principals/foo'), ), $principalBackend->groupMembers); } public function testGetOwner() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertEquals('principals/admin',$principal->getOwner()); } public function testGetGroup() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertNull($principal->getGroup()); } public function testGetACl() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertEquals(array( array( 'privilege' => '{DAV:}read', 'principal' => '{DAV:}authenticated', 'protected' => true, ) ),$principal->getACL()); } /** * @expectedException Sabre\DAV\Exception\MethodNotAllowed */ public function testSetACl() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $principal->setACL(array()); } public function testGetSupportedPrivilegeSet() { $principalBackend = new PrincipalBackend\Mock(); $principal = new Principal($principalBackend, array('uri' => 'principals/admin')); $this->assertNull($principal->getSupportedPrivilegeSet()); } } ����������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Property/�������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0020432�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Property/ACLRestrictionsTest.php��������������������������������0000664�0000000�0000000�00000001361�12670356754�0025014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Property; use Sabre\DAV; use Sabre\HTTP; class ACLRestrictionsTest extends \PHPUnit_Framework_TestCase { function testConstruct() { $prop = new AclRestrictions(); $this->assertInstanceOf('Sabre\DAVACL\Property\ACLRestrictions', $prop); } function testSerializeEmpty() { $dom = new \DOMDocument('1.0'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $acl = new AclRestrictions(); $acl->serialize(new DAV\Server(), $root); $xml = $dom->saveXML(); $expected = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"><d:grant-only/><d:no-invert/></d:root> '; $this->assertEquals($expected, $xml); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Property/ACLTest.php��������������������������������������������0000664�0000000�0000000�00000016371�12670356754�0022412�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Property; use Sabre\DAV; use Sabre\HTTP; class ACLTest extends \PHPUnit_Framework_TestCase { function testConstruct() { $acl = new Acl(array()); $this->assertInstanceOf('Sabre\DAVACL\Property\ACL', $acl); } function testSerializeEmpty() { $dom = new \DOMDocument('1.0'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $acl = new Acl(array()); $acl->serialize(new DAV\Server(), $root); $xml = $dom->saveXML(); $expected = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"/> '; $this->assertEquals($expected, $xml); } function testSerialize() { $dom = new \DOMDocument('1.0'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $privileges = array( array( 'principal' => 'principals/evert', 'privilege' => '{DAV:}write', 'uri' => 'articles', ), array( 'principal' => 'principals/foo', 'privilege' => '{DAV:}read', 'uri' => 'articles', 'protected' => true, ), ); $acl = new Acl($privileges); $acl->serialize(new DAV\Server(), $root); $dom->formatOutput = true; $xml = $dom->saveXML(); $expected = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:ace> <d:principal> <d:href>/principals/evert/</d:href> </d:principal> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> </d:ace> <d:ace> <d:principal> <d:href>/principals/foo/</d:href> </d:principal> <d:grant> <d:privilege> <d:read/> </d:privilege> </d:grant> <d:protected/> </d:ace> </d:root> '; $this->assertEquals($expected, $xml); } function testSerializeSpecialPrincipals() { $dom = new \DOMDocument('1.0'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $privileges = array( array( 'principal' => '{DAV:}authenticated', 'privilege' => '{DAV:}write', 'uri' => 'articles', ), array( 'principal' => '{DAV:}unauthenticated', 'privilege' => '{DAV:}write', 'uri' => 'articles', ), array( 'principal' => '{DAV:}all', 'privilege' => '{DAV:}write', 'uri' => 'articles', ), ); $acl = new Acl($privileges); $acl->serialize(new DAV\Server(), $root); $dom->formatOutput = true; $xml = $dom->saveXML(); $expected = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:ace> <d:principal> <d:authenticated/> </d:principal> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> </d:ace> <d:ace> <d:principal> <d:unauthenticated/> </d:principal> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> </d:ace> <d:ace> <d:principal> <d:all/> </d:principal> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> </d:ace> </d:root> '; $this->assertEquals($expected, $xml); } function testUnserialize() { $source = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:ace> <d:principal> <d:href>/principals/evert/</d:href> </d:principal> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> </d:ace> <d:ace> <d:principal> <d:href>/principals/foo/</d:href> </d:principal> <d:grant> <d:privilege> <d:read/> </d:privilege> </d:grant> <d:protected/> </d:ace> </d:root> '; $dom = DAV\XMLUtil::loadDOMDocument($source); $result = Acl::unserialize($dom->firstChild, array()); $this->assertInstanceOf('Sabre\\DAVACL\\Property\\ACL', $result); $expected = array( array( 'principal' => '/principals/evert/', 'protected' => false, 'privilege' => '{DAV:}write', ), array( 'principal' => '/principals/foo/', 'protected' => true, 'privilege' => '{DAV:}read', ), ); $this->assertEquals($expected, $result->getPrivileges()); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testUnserializeNoPrincipal() { $source = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:ace> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> </d:ace> </d:root> '; $dom = DAV\XMLUtil::loadDOMDocument($source); Acl::unserialize($dom->firstChild, array()); } function testUnserializeOtherPrincipal() { $source = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:ace> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> <d:principal><d:authenticated /></d:principal> </d:ace> <d:ace> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> <d:principal><d:unauthenticated /></d:principal> </d:ace> <d:ace> <d:grant> <d:privilege> <d:write/> </d:privilege> </d:grant> <d:principal><d:all /></d:principal> </d:ace> </d:root> '; $dom = DAV\XMLUtil::loadDOMDocument($source); $result = Acl::unserialize($dom->firstChild, array()); $this->assertInstanceOf('Sabre\\DAVACL\\Property\\Acl', $result); $expected = array( array( 'principal' => '{DAV:}authenticated', 'protected' => false, 'privilege' => '{DAV:}write', ), array( 'principal' => '{DAV:}unauthenticated', 'protected' => false, 'privilege' => '{DAV:}write', ), array( 'principal' => '{DAV:}all', 'protected' => false, 'privilege' => '{DAV:}write', ), ); $this->assertEquals($expected, $result->getPrivileges()); } /** * @expectedException Sabre\DAV\Exception\NotImplemented */ function testUnserializeDeny() { $source = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:ace> <d:deny> <d:privilege> <d:write/> </d:privilege> </d:deny> <d:principal><d:href>/principals/evert</d:href></d:principal> </d:ace> </d:root> '; $dom = DAV\XMLUtil::loadDOMDocument($source); Acl::unserialize($dom->firstChild, array()); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testUnserializeMissingPriv() { $source = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:ace> <d:grant> <d:privilege /> </d:grant> <d:principal><d:href>/principals/evert</d:href></d:principal> </d:ace> </d:root> '; $dom = DAV\XMLUtil::loadDOMDocument($source); Acl::unserialize($dom->firstChild, array()); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Property/CurrentUserPrivilegeSetTest.php������������������������0000664�0000000�0000000�00000003402�12670356754�0026606�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Property; use Sabre\DAV; use Sabre\DAV\XMLUtil; use Sabre\HTTP; class CurrentUserPrivilegeSetTest extends \PHPUnit_Framework_TestCase { function testSerialize() { $privileges = array( '{DAV:}read', '{DAV:}write', ); $prop = new CurrentUserPrivilegeSet($privileges); $server = new DAV\Server(); $dom = new \DOMDocument('1.0','utf-8'); $root = $dom->createElementNS('DAV:','d:root'); $dom->appendChild($root); $prop->serialize($server, $root); $xpaths = array( '/d:root' => 1, '/d:root/d:privilege' => 2, '/d:root/d:privilege/d:read' => 1, '/d:root/d:privilege/d:write' => 1, ); // Reloading because PHP DOM sucks $dom2 = XMLUtil::loadDOMDocument($dom->saveXML()); $dxpath = new \DOMXPath($dom2); $dxpath->registerNamespace('d','urn:DAV'); foreach($xpaths as $xpath=>$count) { $this->assertEquals($count, $dxpath->query($xpath)->length, 'Looking for : ' . $xpath . ', we could only find ' . $dxpath->query($xpath)->length . ' elements, while we expected ' . $count); } } function testUnserialize() { $source = '<?xml version="1.0"?> <d:root xmlns:d="DAV:"> <d:privilege> <d:write-properties /> </d:privilege> <d:privilege> <d:read /> </d:privilege> </d:root> '; $dom = DAV\XMLUtil::loadDOMDocument($source); $result = CurrentUserPrivilegeSet::unserialize($dom->firstChild, array()); $this->assertTrue($result->has('{DAV:}read')); $this->assertTrue($result->has('{DAV:}write-properties')); $this->assertFalse($result->has('{DAV:}bind')); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Property/PrincipalTest.php��������������������������������������0000664�0000000�0000000�00000010540�12670356754�0023724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Property; use Sabre\DAV; use Sabre\HTTP; class PrincipalTest extends \PHPUnit_Framework_TestCase { function testSimple() { $principal = new Principal(Principal::UNAUTHENTICATED); $this->assertEquals(Principal::UNAUTHENTICATED, $principal->getType()); $this->assertNull($principal->getHref()); $principal = new Principal(Principal::AUTHENTICATED); $this->assertEquals(Principal::AUTHENTICATED, $principal->getType()); $this->assertNull($principal->getHref()); $principal = new Principal(Principal::HREF,'admin'); $this->assertEquals(Principal::HREF, $principal->getType()); $this->assertEquals('admin',$principal->getHref()); } /** * @depends testSimple * @expectedException Sabre\DAV\Exception */ function testNoHref() { $principal = new Principal(Principal::HREF); } /** * @depends testSimple */ function testSerializeUnAuthenticated() { $prin = new Principal(Principal::UNAUTHENTICATED); $doc = new \DOMDocument(); $root = $doc->createElement('d:principal'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $node = new DAV\SimpleCollection('rootdir'); $server = new DAV\Server($node); $prin->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:principal xmlns:d="DAV:">' . '<d:unauthenticated/>' . '</d:principal> ', $xml); } /** * @depends testSerializeUnAuthenticated */ function testSerializeAuthenticated() { $prin = new Principal(Principal::AUTHENTICATED); $doc = new \DOMDocument(); $root = $doc->createElement('d:principal'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $objectTree = new DAV\Tree(new DAV\SimpleCollection('rootdir')); $server = new DAV\Server($objectTree); $prin->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:principal xmlns:d="DAV:">' . '<d:authenticated/>' . '</d:principal> ', $xml); } /** * @depends testSerializeUnAuthenticated */ function testSerializeHref() { $prin = new Principal(Principal::HREF,'principals/admin'); $doc = new \DOMDocument(); $root = $doc->createElement('d:principal'); $root->setAttribute('xmlns:d','DAV:'); $doc->appendChild($root); $objectTree = new DAV\Tree(new DAV\SimpleCollection('rootdir')); $server = new DAV\Server($objectTree); $prin->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:principal xmlns:d="DAV:">' . '<d:href>/principals/admin</d:href>' . '</d:principal> ', $xml); } function testUnserializeHref() { $xml = '<?xml version="1.0"?> <d:principal xmlns:d="DAV:">' . '<d:href>/principals/admin</d:href>' . '</d:principal>'; $dom = DAV\XMLUtil::loadDOMDocument($xml); $principal = Principal::unserialize($dom->firstChild, array()); $this->assertEquals(Principal::HREF, $principal->getType()); $this->assertEquals('/principals/admin', $principal->getHref()); } function testUnserializeAuthenticated() { $xml = '<?xml version="1.0"?> <d:principal xmlns:d="DAV:">' . ' <d:authenticated />' . '</d:principal>'; $dom = DAV\XMLUtil::loadDOMDocument($xml); $principal = Principal::unserialize($dom->firstChild, array()); $this->assertEquals(Principal::AUTHENTICATED, $principal->getType()); } function testUnserializeUnauthenticated() { $xml = '<?xml version="1.0"?> <d:principal xmlns:d="DAV:">' . ' <d:unauthenticated />' . '</d:principal>'; $dom = DAV\XMLUtil::loadDOMDocument($xml); $principal = Principal::unserialize($dom->firstChild, array()); $this->assertEquals(Principal::UNAUTHENTICATED, $principal->getType()); } /** * @expectedException Sabre\DAV\Exception\BadRequest */ function testUnserializeUnknown() { $xml = '<?xml version="1.0"?> <d:principal xmlns:d="DAV:">' . ' <d:foo />' . '</d:principal>'; $dom = DAV\XMLUtil::loadDOMDocument($xml); Principal::unserialize($dom->firstChild, array()); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/Property/SupportedPrivilegeSetTest.php��������������������������0000664�0000000�0000000�00000004460�12670356754�0026317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL\Property; use Sabre\DAV; use Sabre\HTTP; class SupportedPrivilegeSetTest extends \PHPUnit_Framework_TestCase { function testSimple() { $prop = new SupportedPrivilegeSet(array( 'privilege' => '{DAV:}all', )); $this->assertInstanceOf('Sabre\DAVACL\Property\SupportedPrivilegeSet', $prop); } /** * @depends testSimple */ function testSerializeSimple() { $prop = new SupportedPrivilegeSet(array( 'privilege' => '{DAV:}all', )); $doc = new \DOMDocument(); $root = $doc->createElementNS('DAV:', 'd:supported-privilege-set'); $doc->appendChild($root); $server = new DAV\Server(); $prop->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:supported-privilege-set xmlns:d="DAV:">' . '<d:supported-privilege>' . '<d:privilege>' . '<d:all/>' . '</d:privilege>' . '</d:supported-privilege>' . '</d:supported-privilege-set> ', $xml); } /** * @depends testSimple */ function testSerializeAggregate() { $prop = new SupportedPrivilegeSet(array( 'privilege' => '{DAV:}all', 'abstract' => true, 'aggregates' => array( array( 'privilege' => '{DAV:}read', ), array( 'privilege' => '{DAV:}write', 'description' => 'booh', ), ), )); $doc = new \DOMDocument(); $root = $doc->createElementNS('DAV:', 'd:supported-privilege-set'); $doc->appendChild($root); $server = new DAV\Server(); $prop->serialize($server, $root); $xml = $doc->saveXML(); $this->assertEquals( '<?xml version="1.0"?> <d:supported-privilege-set xmlns:d="DAV:">' . '<d:supported-privilege>' . '<d:privilege>' . '<d:all/>' . '</d:privilege>' . '<d:abstract/>' . '<d:supported-privilege>' . '<d:privilege>' . '<d:read/>' . '</d:privilege>' . '</d:supported-privilege>' . '<d:supported-privilege>' . '<d:privilege>' . '<d:write/>' . '</d:privilege>' . '<d:description>booh</d:description>' . '</d:supported-privilege>' . '</d:supported-privilege>' . '</d:supported-privilege-set> ', $xml); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVACL/SimplePluginTest.php��������������������������������������������0000664�0000000�0000000�00000022435�12670356754�0022575�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\DAVACL; use Sabre\DAV; use Sabre\HTTP; require_once 'Sabre/DAVACL/MockPrincipal.php'; require_once 'Sabre/DAVACL/MockACLNode.php'; class SimplePluginTest extends \PHPUnit_Framework_TestCase { function testValues() { $aclPlugin = new Plugin(); $this->assertEquals('acl',$aclPlugin->getPluginName()); $this->assertEquals( array('access-control', 'calendarserver-principal-property-search'), $aclPlugin->getFeatures() ); $this->assertEquals( array( '{DAV:}expand-property', '{DAV:}principal-property-search', '{DAV:}principal-search-property-set' ), $aclPlugin->getSupportedReportSet('')); $this->assertEquals(array('ACL'), $aclPlugin->getMethods('')); } function testGetFlatPrivilegeSet() { $expected = array( '{DAV:}all' => array( 'privilege' => '{DAV:}all', 'abstract' => true, 'aggregates' => array( '{DAV:}read', '{DAV:}write', ), 'concrete' => null, ), '{DAV:}read' => array( 'privilege' => '{DAV:}read', 'abstract' => false, 'aggregates' => array( '{DAV:}read-acl', '{DAV:}read-current-user-privilege-set', ), 'concrete' => '{DAV:}read', ), '{DAV:}read-acl' => array( 'privilege' => '{DAV:}read-acl', 'abstract' => false, 'aggregates' => array(), 'concrete' => '{DAV:}read-acl', ), '{DAV:}read-current-user-privilege-set' => array( 'privilege' => '{DAV:}read-current-user-privilege-set', 'abstract' => false, 'aggregates' => array(), 'concrete' => '{DAV:}read-current-user-privilege-set', ), '{DAV:}write' => array( 'privilege' => '{DAV:}write', 'abstract' => false, 'aggregates' => array( '{DAV:}write-acl', '{DAV:}write-properties', '{DAV:}write-content', '{DAV:}bind', '{DAV:}unbind', '{DAV:}unlock', ), 'concrete' => '{DAV:}write', ), '{DAV:}write-acl' => array( 'privilege' => '{DAV:}write-acl', 'abstract' => false, 'aggregates' => array(), 'concrete' => '{DAV:}write-acl', ), '{DAV:}write-properties' => array( 'privilege' => '{DAV:}write-properties', 'abstract' => false, 'aggregates' => array(), 'concrete' => '{DAV:}write-properties', ), '{DAV:}write-content' => array( 'privilege' => '{DAV:}write-content', 'abstract' => false, 'aggregates' => array(), 'concrete' => '{DAV:}write-content', ), '{DAV:}unlock' => array( 'privilege' => '{DAV:}unlock', 'abstract' => false, 'aggregates' => array(), 'concrete' => '{DAV:}unlock', ), '{DAV:}bind' => array( 'privilege' => '{DAV:}bind', 'abstract' => false, 'aggregates' => array(), 'concrete' => '{DAV:}bind', ), '{DAV:}unbind' => array( 'privilege' => '{DAV:}unbind', 'abstract' => false, 'aggregates' => array(), 'concrete' => '{DAV:}unbind', ), ); $plugin = new Plugin(); $server = new DAV\Server(); $server->addPlugin($plugin); $this->assertEquals($expected, $plugin->getFlatPrivilegeSet('')); } function testCurrentUserPrincipalsNotLoggedIn() { $acl = new Plugin(); $server = new DAV\Server(); $server->addPlugin($acl); $this->assertEquals(array(),$acl->getCurrentUserPrincipals()); } function testCurrentUserPrincipalsSimple() { $tree = array( new DAV\SimpleCollection('principals', array( new MockPrincipal('admin','principals/admin'), )) ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->addPlugin($acl); $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV'); $server->addPlugin($auth); //forcing login $auth->beforeMethod(new HTTP\Request(), new HTTP\Response()); $this->assertEquals(array('principals/admin'),$acl->getCurrentUserPrincipals()); } function testCurrentUserPrincipalsGroups() { $tree = array( new DAV\SimpleCollection('principals', array( new MockPrincipal('admin','principals/admin',array('principals/administrators', 'principals/everyone')), new MockPrincipal('administrators','principals/administrators',array('principals/groups'), array('principals/admin')), new MockPrincipal('everyone','principals/everyone',array(), array('principals/admin')), new MockPrincipal('groups','principals/groups',array(), array('principals/administrators')), )) ); $acl = new Plugin(); $server = new DAV\Server($tree); $server->addPlugin($acl); $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV'); $server->addPlugin($auth); //forcing login $auth->beforeMethod(new HTTP\Request(), new HTTP\Response()); $expected = array( 'principals/admin', 'principals/administrators', 'principals/everyone', 'principals/groups', ); $this->assertEquals($expected,$acl->getCurrentUserPrincipals()); // The second one should trigger the cache and be identical $this->assertEquals($expected,$acl->getCurrentUserPrincipals()); } function testGetACL() { $acl = array( array( 'principal' => 'principals/admin', 'privilege' => '{DAV:}read', ), array( 'principal' => 'principals/admin', 'privilege' => '{DAV:}write', ), ); $tree = array( new MockACLNode('foo',$acl), ); $server = new DAV\Server($tree); $aclPlugin = new Plugin(); $server->addPlugin($aclPlugin); $this->assertEquals($acl,$aclPlugin->getACL('foo')); } function testGetCurrentUserPrivilegeSet() { $acl = array( array( 'principal' => 'principals/admin', 'privilege' => '{DAV:}read', ), array( 'principal' => 'principals/user1', 'privilege' => '{DAV:}read', ), array( 'principal' => 'principals/admin', 'privilege' => '{DAV:}write', ), ); $tree = array( new MockACLNode('foo',$acl), new DAV\SimpleCollection('principals', array( new MockPrincipal('admin','principals/admin'), )), ); $server = new DAV\Server($tree); $aclPlugin = new Plugin(); $server->addPlugin($aclPlugin); $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV'); $server->addPlugin($auth); //forcing login $auth->beforeMethod(new HTTP\Request(), new HTTP\Response()); $expected = array( '{DAV:}write', '{DAV:}write-acl', '{DAV:}write-properties', '{DAV:}write-content', '{DAV:}bind', '{DAV:}unbind', '{DAV:}unlock', '{DAV:}read', '{DAV:}read-acl', '{DAV:}read-current-user-privilege-set', ); $this->assertEquals($expected,$aclPlugin->getCurrentUserPrivilegeSet('foo')); } function testCheckPrivileges() { $acl = array( array( 'principal' => 'principals/admin', 'privilege' => '{DAV:}read', ), array( 'principal' => 'principals/user1', 'privilege' => '{DAV:}read', ), array( 'principal' => 'principals/admin', 'privilege' => '{DAV:}write', ), ); $tree = array( new MockACLNode('foo',$acl), new DAV\SimpleCollection('principals', array( new MockPrincipal('admin','principals/admin'), )), ); $server = new DAV\Server($tree); $aclPlugin = new Plugin(); $server->addPlugin($aclPlugin); $auth = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(),'SabreDAV'); $server->addPlugin($auth); //forcing login //$auth->beforeMethod('GET','/'); $this->assertFalse($aclPlugin->checkPrivileges('foo', array('{DAV:}read'), Plugin::R_PARENT, false)); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/DAVServerTest.php������������������������������������������������������0000664�0000000�0000000�00000016032�12670356754�0021030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre; use Sabre\HTTP\Request, Sabre\HTTP\Response, Sabre\HTTP\Sapi; /** * This class may be used as a basis for other webdav-related unittests. * * This class is supposed to provide a reasonably big framework to quickly get * a testing environment running. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ abstract class DAVServerTest extends \PHPUnit_Framework_TestCase { protected $setupCalDAV = false; protected $setupCardDAV = false; protected $setupACL = false; protected $setupCalDAVSharing = false; protected $setupCalDAVScheduling = false; protected $setupCalDAVSubscriptions = false; protected $setupCalDAVICSExport = false; protected $setupLocks = false; protected $setupFiles = false; /** * An array with calendars. Every calendar should have * - principaluri * - uri */ protected $caldavCalendars = array(); protected $caldavCalendarObjects = array(); protected $carddavAddressBooks = array(); protected $carddavCards = array(); /** * @var Sabre\DAV\Server */ protected $server; protected $tree = array(); protected $caldavBackend; protected $carddavBackend; protected $principalBackend; protected $locksBackend; /** * @var Sabre\CalDAV\Plugin */ protected $caldavPlugin; /** * @var Sabre\CardDAV\Plugin */ protected $carddavPlugin; /** * @var Sabre\DAVACL\Plugin */ protected $aclPlugin; /** * @var Sabre\CalDAV\SharingPlugin */ protected $caldavSharingPlugin; /** * CalDAV scheduling plugin * * @var CalDAV\Schedule\Plugin */ protected $caldavSchedulePlugin; /** * @var Sabre\DAV\Auth\Plugin */ protected $authPlugin; /** * @var Sabre\DAV\Locks\Plugin */ protected $locksPlugin; /** * If this string is set, we will automatically log in the user with this * name. */ protected $autoLogin = null; function setUp() { $this->setUpBackends(); $this->setUpTree(); $this->server = new DAV\Server($this->tree); $this->server->sapi = new HTTP\SapiMock(); $this->server->debugExceptions = true; if ($this->setupCalDAV) { $this->caldavPlugin = new CalDAV\Plugin(); $this->server->addPlugin($this->caldavPlugin); } if ($this->setupCalDAVSharing) { $this->caldavSharingPlugin = new CalDAV\SharingPlugin(); $this->server->addPlugin($this->caldavSharingPlugin); } if ($this->setupCalDAVScheduling) { $this->caldavSchedulePlugin = new CalDAV\Schedule\Plugin(); $this->server->addPlugin($this->caldavSchedulePlugin); } if ($this->setupCalDAVSubscriptions) { $this->server->addPlugin(new CalDAV\Subscriptions\Plugin()); } if ($this->setupCalDAVICSExport) { $this->caldavICSExportPlugin = new CalDAV\ICSExportPlugin(); $this->server->addPlugin($this->caldavICSExportPlugin); } if ($this->setupCardDAV) { $this->carddavPlugin = new CardDAV\Plugin(); $this->server->addPlugin($this->carddavPlugin); } if ($this->setupACL) { $this->aclPlugin = new DAVACL\Plugin(); $this->server->addPlugin($this->aclPlugin); } if ($this->setupLocks) { $this->locksPlugin = new DAV\Locks\Plugin( $this->locksBackend ); $this->server->addPlugin($this->locksPlugin); } if ($this->autoLogin) { $authBackend = new DAV\Auth\Backend\Mock(); $authBackend->defaultUser = $this->autoLogin; $this->authPlugin = new DAV\Auth\Plugin($authBackend, 'SabreDAV'); $this->server->addPlugin($this->authPlugin); // This will trigger the actual login procedure $this->authPlugin->beforeMethod(new Request(), new Response()); } } /** * Makes a request, and returns a response object. * * You can either pass an instance of Sabre\HTTP\Request, or an array, * which will then be used as the _SERVER array. * * @param array|\Sabre\HTTP\Request $request * @return \Sabre\HTTP\Response */ function request($request) { if (is_array($request)) { $request = HTTP\Request::createFromServerArray($request); } $this->server->httpRequest = $request; $this->server->httpResponse = new HTTP\ResponseMock(); $this->server->exec(); return $this->server->httpResponse; } /** * Override this to provide your own Tree for your test-case. */ function setUpTree() { if ($this->setupCalDAV) { $this->tree[] = new CalDAV\CalendarRoot( $this->principalBackend, $this->caldavBackend ); } if ($this->setupCardDAV) { $this->tree[] = new CardDAV\AddressBookRoot( $this->principalBackend, $this->carddavBackend ); } if ($this->setupCardDAV || $this->setupCalDAV) { $this->tree[] = new DAVACL\PrincipalCollection( $this->principalBackend ); } if ($this->setupFiles) { $this->tree[] = new DAV\Mock\Collection('files'); } } function setUpBackends() { if ($this->setupCalDAVSharing && is_null($this->caldavBackend)) { $this->caldavBackend = new CalDAV\Backend\MockSharing($this->caldavCalendars, $this->caldavCalendarObjects); } if ($this->setupCalDAVSubscriptions && is_null($this->caldavBackend)) { $this->caldavBackend = new CalDAV\Backend\MockSubscriptionSupport($this->caldavCalendars, $this->caldavCalendarObjects); } if ($this->setupCalDAV && is_null($this->caldavBackend)) { if ($this->setupCalDAVScheduling) { $this->caldavBackend = new CalDAV\Backend\MockScheduling($this->caldavCalendars, $this->caldavCalendarObjects); } else { $this->caldavBackend = new CalDAV\Backend\Mock($this->caldavCalendars, $this->caldavCalendarObjects); } } if ($this->setupCardDAV && is_null($this->carddavBackend)) { $this->carddavBackend = new CardDAV\Backend\Mock($this->carddavAddressBooks, $this->carddavCards); } if ($this->setupCardDAV || $this->setupCalDAV) { $this->principalBackend = new DAVACL\PrincipalBackend\Mock(); } if ($this->setupLocks) { $this->locksBackend = new DAV\Locks\Backend\Mock(); } } function assertHTTPStatus($expectedStatus, HTTP\Request $req) { $resp = $this->request($req); $this->assertEquals((int)$expectedStatus, (int)$resp->status,'Incorrect HTTP status received: ' . $resp->body); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/HTTP/������������������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0016433�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/HTTP/ResponseMock.php��������������������������������������������������0000664�0000000�0000000�00000000651�12670356754�0021556�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\HTTP; /** * HTTP Response Mock object * * This class exists to make the transition to sabre/http easier. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class ResponseMock extends Response { /** * Making these public. */ public $body; public $status; } ���������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/HTTP/SapiMock.php������������������������������������������������������0000664�0000000�0000000�00000001047�12670356754�0020654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre\HTTP; /** * HTTP Response Mock object * * This class exists to make the transition to sabre/http easier. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class SapiMock extends Sapi { static $sent = 0; /** * Overriding this so nothing is ever echo'd. * * @return void */ static function sendResponse(\Sabre\HTTP\ResponseInterface $r) { self::$sent++; } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/Sabre/TestUtil.php�����������������������������������������������������������0000664�0000000�0000000�00000002260�12670356754�0020142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php namespace Sabre; class TestUtil { /** * This function deletes all the contents of the temporary directory. * * @return void */ static function clearTempDir() { self::deleteTree(SABRE_TEMPDIR,false); } static private function deleteTree($path,$deleteRoot = true) { foreach(scandir($path) as $node) { if ($node=='.' || $node=='..') continue; $myPath = $path.'/'. $node; if (is_file($myPath)) { unlink($myPath); } else { self::deleteTree($myPath); } } if ($deleteRoot) { rmdir($path); } } static function getMySQLDB() { try { $pdo = new \PDO(SABRE_MYSQLDSN,SABRE_MYSQLUSER,SABRE_MYSQLPASS); $pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); return $pdo; } catch (\PDOException $e) { return null; } } static function getSQLiteDB() { $pdo = new \PDO('sqlite:'.SABRE_TEMPDIR.'/pdobackend'); $pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); return $pdo; } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/bootstrap.php����������������������������������������������������������������0000664�0000000�0000000�00000001646�12670356754�0017355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php set_include_path(__DIR__ . '/../lib/' . PATH_SEPARATOR . __DIR__ . PATH_SEPARATOR . get_include_path()); $autoLoader = include __DIR__ . '/../vendor/autoload.php'; // SabreDAV tests auto loading $autoLoader->add('Sabre\\', __DIR__); // VObject tests auto loadiong $autoLoader->addPsr4('Sabre\\VObject\\',__DIR__ . '/../vendor/sabre/vobject/tests/VObject'); date_default_timezone_set('UTC'); $config = [ 'SABRE_TEMPDIR' => dirname(__FILE__) . '/temp/', 'SABRE_HASSQLITE' => in_array('sqlite',PDO::getAvailableDrivers()), 'SABRE_HASMYSQL' => in_array('mysql',PDO::getAvailableDrivers()), 'SABRE_MYSQLDSN' => 'mysql:host=127.0.0.1;dbname=sabredav', 'SABRE_MYSQLUSER' => 'root', 'SABRE_MYSQLPASS' => '', ]; foreach($config as $key=>$value) { if (!defined($key)) define($key, $value); } if (!file_exists(SABRE_TEMPDIR)) mkdir(SABRE_TEMPDIR); if (file_exists('.sabredav')) unlink('.sabredav'); ������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/phpcs/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�12670356754�0015735�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/phpcs/ruleset.xml������������������������������������������������������������0000664�0000000�0000000�00000003642�12670356754�0020147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0"?> <ruleset name="sabre.php"> <description>sabre.io codesniffer ruleset</description> <!-- Include the whole PSR-1 standard --> <rule ref="PSR1" /> <!-- All PHP files MUST use the Unix LF (linefeed) line ending. --> <rule ref="Generic.Files.LineEndings"> <properties> <property name="eolChar" value="\n"/> </properties> </rule> <!-- The closing ?> tag MUST be omitted from files containing only PHP. --> <rule ref="Zend.Files.ClosingTag"/> <!-- There MUST NOT be trailing whitespace at the end of non-blank lines. --> <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"> <properties> <property name="ignoreBlankLines" value="true"/> </properties> </rule> <!-- There MUST NOT be more than one statement per line. --> <rule ref="Generic.Formatting.DisallowMultipleStatements"/> <rule ref="Generic.WhiteSpace.ScopeIndent"> <properties> <property name="ignoreIndentationTokens" type="array" value="T_COMMENT,T_DOC_COMMENT"/> </properties> </rule> <rule ref="Generic.WhiteSpace.DisallowTabIndent"/> <!-- PHP keywords MUST be in lower case. --> <rule ref="Generic.PHP.LowerCaseKeyword"/> <!-- The PHP constants true, false, and null MUST be in lower case. --> <rule ref="Generic.PHP.LowerCaseConstant"/> <!-- <rule ref="Squiz.Scope.MethodScope"/> --> <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/> <!-- In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. --> <!-- <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing"> <properties> <property name="equalsSpacing" value="1"/> </properties> </rule> <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint"> <severity>0</severity> </rule> --> <rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/> </ruleset> ����������������������������������������������������������������������������������������������sabre-dav-2.1.10/tests/phpunit.xml������������������������������������������������������������������0000664�0000000�0000000�00000002221�12670356754�0017026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<phpunit colors="true" bootstrap="bootstrap.php" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" strict="true" > <testsuite name="sabre-vobject"> <directory>../vendor/sabre/vobject/tests/VObject</directory> </testsuite> <testsuite name="sabre-event"> <directory>../vendor/sabre/event/tests/</directory> </testsuite> <testsuite name="sabre-http"> <directory>../vendor/sabre/http/tests/HTTP</directory> </testsuite> <testsuite name="sabre-dav"> <directory>Sabre/DAV</directory> </testsuite> <testsuite name="sabre-davacl"> <directory>Sabre/DAVACL</directory> </testsuite> <testsuite name="sabre-caldav"> <directory>Sabre/CalDAV</directory> </testsuite> <testsuite name="sabre-carddav"> <directory>Sabre/CardDAV</directory> </testsuite> <filter> <whitelist addUncoveredFilesFromWhitelist="true"> <directory suffix=".php">../lib/</directory> <exclude> <file>../lib/Sabre/autoload.php</file> <file>../lib/Sabre/VObject/includes.php</file> </exclude> </whitelist> </filter> </phpunit> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������