diff --git a/htdocs/includes/sabre/sabre/dav/.gitignore b/htdocs/includes/sabre/sabre/dav/.gitignore index 6cf24588320..499f7b689c2 100644 --- a/htdocs/includes/sabre/sabre/dav/.gitignore +++ b/htdocs/includes/sabre/sabre/dav/.gitignore @@ -41,3 +41,5 @@ docs/wikidocs # Mac .DS_Store + +.php_cs.cache diff --git a/htdocs/includes/sabre/sabre/dav/.php_cs.dist b/htdocs/includes/sabre/sabre/dav/.php_cs.dist new file mode 100644 index 00000000000..8d61ee25924 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/.php_cs.dist @@ -0,0 +1,12 @@ +getFinder() + ->exclude('vendor') + ->in(__DIR__); +$config->setRules([ + '@PSR1' => true, + '@Symfony' =>true +]); + +return $config; \ No newline at end of file diff --git a/htdocs/includes/sabre/sabre/dav/.travis.yml b/htdocs/includes/sabre/sabre/dav/.travis.yml index 85637048ad4..c98ea59ef4d 100644 --- a/htdocs/includes/sabre/sabre/dav/.travis.yml +++ b/htdocs/includes/sabre/sabre/dav/.travis.yml @@ -1,35 +1,51 @@ language: php +sudo: required php: - - 5.5 - - 5.6 - 7.0 - 7.1 - + - 7.2 + - 7.3 env: + global: + - SABRE_MYSQLUSER="root" + - SABRE_MYSQLPASS="" + - SABRE_MYSQLDSN="mysql:host=127.0.0.1;dbname=sabredav_test" + - RUN_PHPSTAN="FALSE" matrix: - - LOWEST_DEPS="" TEST_DEPS="" - - LOWEST_DEPS="--prefer-lowest" TEST_DEPS="tests/Sabre/" + - LOWEST_DEPS="" TEST_DEPS="" WITH_COVERAGE="--coverage-clover=coverage.xml" + - LOWEST_DEPS="--prefer-lowest" TEST_DEPS="tests/Sabre/" $WITH_COVERAGE="" + +matrix: + include: + - name: 'PHPStan' + php: 7.2 + env: RUN_PHPSTAN="TRUE" services: - mysql - postgresql -sudo: false +install: + - if [ $RUN_PHPSTAN == "TRUE" ]; then wget https://github.com/phpstan/phpstan/releases/download/0.11.8/phpstan.phar; fi before_script: - - mysql -e 'create database sabredav_test' +# - mysql -u root -h 127.0.0.1 sabredav_test -e 'SELECT VERSION();' +#- mysql -u root -h 127.0.0.1 -e 'create database sabredav_test' - psql -c "create database sabredav_test" -U postgres - psql -c "create user sabredav with PASSWORD 'sabredav';GRANT ALL PRIVILEGES ON DATABASE sabredav_test TO sabredav" -U postgres - # - composer self-update - composer update --prefer-dist $LOWEST_DEPS -# addons: -# postgresql: "9.5" +addons: + postgresql: "9.5" script: - - ./bin/phpunit --configuration tests/phpunit.xml.dist $TEST_DEPS - - ./bin/sabre-cs-fixer fix . --dry-run --diff + - if [ $RUN_PHPSTAN == "FALSE" ]; then ./bin/phpunit --verbose --configuration tests/phpunit.xml.dist $WITH_COVERAGE $TEST_DEPS; fi + - if [ $RUN_PHPSTAN == "FALSE" ]; then rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini; fi + - if [ $RUN_PHPSTAN == "TRUE" ]; then php phpstan.phar analyse -c phpstan.neon lib; fi + +after_success: + - bash <(curl -s https://codecov.io/bash) cache: directories: diff --git a/htdocs/includes/sabre/sabre/dav/CHANGELOG.md b/htdocs/includes/sabre/sabre/dav/CHANGELOG.md new file mode 100644 index 00000000000..455b3ae5e6e --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/CHANGELOG.md @@ -0,0 +1,2497 @@ +ChangeLog +========= + +4.0.2 (2019-10-18) +------------------------- +* Fix error with PHP 7.4 +* CardDAV: Fix content-type for Thunderbird + + +4.0.1 (2019-08-20) +------------------------- +* TemporaryFileFilterPlugin: Fix Strict Error +* CalDAV\Plugin: Fix null path + + +4.0.0 (2019-07-01) +------------------------- +* Lock: Support lock timeout value Infinity +* Lock: Hide lock token in lock discovery when not set +* BrowserPlugin: Show display name of nodes +* FSExt: Fix folder (file) move issue if rename fails +* IMipPlugin: Add sender name in invite mail headers +* IMipPlugin: Fix email subject and recipient +* Fix issues with empty content-type header +* Apply new code style +* Fix for litmus test suite - test case: props propfind_invalid2 +* Depend on sabre/xml 2.0.1 +* Depend on sabre/http 5.0 +* Now supports PHP 7.3 +* Now requires PHP 7. +* Using `strict_types` in every php file. +* #896: Using the [sabre/event][evnt] `WildcardEmitter`. This allows event + handlers to listen to events using a wildcard. +* #896: Event listeners that in the past listened to `beforeMethod` or `method` + no longer get called. They must listen to `beforeMethod:*` and `method:*` now. +* #322: Imap authentication backend. (@c0d3z3r0). +* #889: Support for selective property querying in CardDAV's addressbook-query. + (@DeepDiver1975). +* #982: Make sure that files that are siblings of directories, are reported + as files (@nickvergessen) + + +4.0.0-beta1 (2019-05-08) +------------------------- +* Lock: Support lock timeout value Infinity +* Lock: Hide lock token in lock discovery when not set +* BrowserPlugin: Show display name of nodes +* FSExt: Fix folder (file) move issue if rename fails +* IMipPlugin: Add sender name in invite mail headers +* IMipPlugin: Fix email subject and recipient + + +4.0.0-alpha5 (2018-10-15) +------------------------- +* Fix issues with empty content-type header + + +4.0.0-alpha4 (2018-10-12) +------------------------- +* Apply new code style +* Fix for litmus test suite - test case: props propfind_invalid2 +* Depend on sabre/xml 2.0.1 + + +4.0.0-alpha3 (2018-10-05) +------------------------- +* Fixes for PHP 7.3 +* Depend on sabre/http 5.0 + + +4.0.0-alpha2 (2018-09-27) +------------------------- +* Now supports PHP 7.3 + + +4.0.0-alpha1 (2018-06-05) +------------------------- + +* Now requires PHP 7. +* Using `strict_types` in every php file. +* #896: Using the [sabre/event][evnt] `WildcardEmitter`. This allows event + handlers to listen to events using a wildcard. +* #896: Event listeners that in the past listened to `beforeMethod` or `method` + no longer get called. They must listen to `beforeMethod:*` and `method:*` now. +* #322: Imap authentication backend. (@c0d3z3r0). +* #889: Support for selective property querying in CardDAV's addressbook-query. + (@DeepDiver1975). +* #982: Make sure that files that are siblings of directories, are reported + as files (@nickvergessen) + + +3.3.0-alpha1 (2018-06-04) +------------------------- + +* SimpleCollection can now take arrays and strings as argument for super + simple tree creation. +* Added `Sabre\DAV\Server::start()`. This replaces `::exec()`. `::exec()` + is now deprecated, but we're keeping it around for a year or two to make + the transition easier. +* `getChildren()` function in any collection may now return an iterator + instead of an array. This can result in memory savings for large + collections. +* `Tree::getChildren()` now returns an Iterator instead of an array. +* Added `$overrideName` to all `Sabre\DAV\FS` and `Sabre\DAV\FSExt` classes, + so users can specify under what name these nodes show up in the tree. +* #889 Added support for filtering vCard properties in the addressbook-query + REPORT (@DeepDiver1975). +* #918: Add a lot of sqlite indexes. This should speed up sqlite-based + installations quite a bit. +* #982: Make sure that files that are siblings of directories, are reported + as files (@nickvergessen) +* #1058: Don't open file resource on HEAD request (@icewind1991) +* #1031: Fix copyNode for case of file named 0 (@phil-davis) + + +3.2.3 (????-??-??) +------------------ + +* #982: Make sure that files that are siblings of directories, are reported + as files (@nickvergessen) + + +3.2.2 (2017-02-14) +------------------ + +* #943: Fix CardDAV XML reporting bug, which was affecting several CardDAV + clients. Bug was introduced in 3.2.1. +* The zip release ships with [sabre/vobject 4.1.2][vobj], + [sabre/http 4.2.2][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.2.0][uri] and [sabre/xml 1.5.0][xml]. + + +3.2.1 (2017-01-28) +------------------ + +* #877: Fix for syncing large calendars when using the Sqlite PDO backend. + (@theseer). +* #889 Added support for filtering vCard properties in the addressbook-query + REPORT (@DeepDiver1975). +* The zip release ships with [sabre/vobject 4.1.2][vobj], + [sabre/http 4.2.2][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.2.0][uri] and [sabre/xml 1.5.0][xml]. + + +3.2.0 (2016-06-27) +------------------ + +* The default ACL rules allow an unauthenticated user to read information + about nodes that don't have their own ACL defined. This was a security + problem. +* The zip release ships with [sabre/vobject 4.1.0][vobj], + [sabre/http 4.2.1][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.1.0][uri] and [sabre/xml 1.4.2][xml]. + + +3.2.0-beta1 (2016-05-20) +------------------------ + +* #833: Calendars throw exceptions when the sharing plugin is not enabled. +* #834: Return vCards exactly as they were stored if we don't need to convert + in between versions. +* The zip release ships with [sabre/vobject 4.1.0][vobj], + [sabre/http 4.2.1][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.1.0][uri] and [sabre/xml 1.4.1][xml]. + + +3.2.0-alpha1 (2016-05-09) +------------------------- + +* Database changes for CalDAV. If you are using the CalDAV PDO backends, you + must migrate. Run `./bin/migrateto32.php` for more info. +* Support for WebDAV Resource Sharing, an upcoming standard. +* Added support for sharing in the CalDAV PDO backend! Users can now invite + others to their calendar and give them read/read-write access! +* #397: Support for PSR-3. You can now log exceptions with your favourite + psr3-compatible logging tool. +* #825: Actual proper, tested support for PostgreSQL. We require version 9.5. +* Removed database migration script for sabre/dav 1.7. To update from that + version you now first need to update to sabre/dav 3.1. +* Removed deprecated function: `Sabre\DAV\Auth\Plugin::getCurrentUser()`. +* #774: Fixes for getting free disk space on Windows. +* #803: Major changes in the sharing API. If you were using an old sabre/dav + sharing api, head to the website for more detailed migration notes. +* #657: Support for optional auth using `{DAV:}unauthorized` and `{DAV:}all` + privileges. This allows you to assign a privilege to a resource, allowing + non-authenticated users to access it. For instance, this could allow you + to create a public read-only collection. +* #812 #814: ICS/VCF exporter now includes a more useful filename in its + `Content-Disposition` header. (@Xenopathic). +* #801: BC break: If you were using the `Href` object before, it's behavior + now changed a bit, and `LocalHref` was added to replace the old, default + behavior of `Href`. See the migration doc for more info. +* Removed `Sabre\DAVACL\Plugin::$allowAccessToNodesWithoutACL` setting. + Instead, you can provide a set of default ACL rules with + `Sabre\DAVACL\Plugin::setDefaultAcl()`. +* Introduced `Sabre\DAVACL\ACLTrait` which contains a default implementation + of `Sabre\DAV\IACL` with some sane defaults. We're using this trait all over + the place now, reducing the amount of boilerplate. +* Plugins can now control the "Supported Privilege Set". +* Added Sharing, ICSExport and VCFExport plugins to `groupwareserver.php` + example. +* The `{DAV:}all` privilege is now no longer abstract, so it can be assigned + directly. We're using the `{DAV:}all` privilege now in a lot of cases where + we before assigned both `{DAV:}read` and `{DAV:}write`. +* Resources that are not collections no longer support the `{DAV:}bind` and + `{DAV:}unbind` privileges. +* Corrected the CalDAV-scheduling related privileges. +* Doing an `UNLOCK` no longer requires the `{DAV:}write-content` privilege. +* Added a new `getPrincipalByUri` plugin event. Allowing plugins to request + quickly where a principal lives on a server. +* Renamed `phpunit.xml` to `phpunit.xml.dist` to make local modifications easy. +* Functionality from `IShareableCalendar` is merged into `ISharedCalendar`. +* #751: Fixed XML responses from failing `MKCOL` requests. +* #600: Support for `principal-match` ACL `REPORT`. +* #599: Support for `acl-principal-prop-set` ACL `REPORT`. +* #798: Added an index on `firstoccurence` field in MySQL CalDAV backend. This + should speed up common calendar-query requests. +* #759: DAV\Client is now able to actually correctly resolve relative urls. +* #671: We are no longer checking the `read-free-busy` privilege on individual + calendars during freebusy operations in the scheduling plugin. Instead, we + check the `schedule-query-freebusy` privilege on the target users' inbox, + which validates access for the entire account, per the spec. +* The zip release ships with [sabre/vobject 4.1.0][vobj], + [sabre/http 4.2.1][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.1.0][uri] and [sabre/xml 1.4.1][xml]. + + +3.1.5 (????-??-??) +------------------ + +* Fixed: Creating a new calendar on some MySQL configurations caused an error. +* #889 Added support for filtering vCard properties in the addressbook-query + REPORT (@DeepDiver1975). + + + +3.1.4 (2016-05-28) +------------------ + +* #834: Backport from `master`: Return vCards exactly as they were stored if + we don't need to convert in between versions. This should speed up many + large addressbook syncs sometimes up to 50%. +* The zip release ships with [sabre/vobject 4.1.0][vobj], + [sabre/http 4.2.1][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.1.0][uri] and [sabre/xml 1.4.2][xml]. + + +3.1.3 (2016-04-06) +------------------ + +* Set minimum libxml version to 2.7.0 in `composer.json`. +* #805: It wasn't possible to create calendars that hold events, journals and + todos using MySQL, because the `components` column was 1 byte too small. +* The zip release ships with [sabre/vobject 4.1.0][vobj], + [sabre/http 4.2.1][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.1.0][uri] and [sabre/xml 1.4.1][xml]. + + +3.1.2 (2016-03-12) +------------------ + +* #784: Sync logs for address books were not correctly cleaned up after + deleting them. +* #787: Cannot use non-seekable stream-wrappers with range requests. +* Faster XML parsing and generating due to sabre/xml update. +* #793: The Sqlite schema is now more strict and more similar to the MySQL + schema. This solves a problem within Baikal. +* The zip release ships with [sabre/vobject 4.0.3][vobj], + [sabre/http 4.2.1][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.1.0][uri] and [sabre/xml 1.4.1][xml]. + + +3.1.1 (2016-01-25) +------------------ + +* #755: The brower plugin and some operations would break when scheduling and + delegation would both be enabled. +* #757: A bunch of unittest improvements (@jakobsack). +* The zip release ships with [sabre/vobject 4.0.2][vobj], + [sabre/http 4.2.1][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.3.0][xml]. + + +3.1.0 (2016-01-06) +------------------ + +* Better error message when the browser plugin is not enabled. +* Added a super minimal server example. +* #730: Switched all mysql tables to `utf8mb4` character set, allowing you to + use emoji in some tables where you couldn't before. +* #710: Provide an Auth backend that acts as a helper for people implementing + OAuth2 Bearer token. (@fkooman). +* #729: Not all calls to `Sabre\DAV\Tree::getChildren()` were properly cached. +* #727: Added another workaround to make CalDAV work for Windows 10 clients. +* #742: Fixes to make sure that vobject 4 is correctly supported. +* #726: Better error reporting in `Client::propPatch`. We're now throwing + exceptions. +* #608: When a HTTP error is triggered during `Client:propFind`, we're now + throwing `Sabre\HTTP\ClientHttpException` instead of `Sabre\DAV\Exception`. + This new exception contains a LOT more information about the problem. +* #721: Events are now handled in the correct order for `COPY` requests. + Before this subtle bugs could appear that could cause data-loss. +* #747: Now throwing exceptions and setting the HTTP status to 500 in subtle + cases where no other plugin set a correct HTTP status. +* #686: Corrected PDO principal backend's findByURI for email addresses that + don't match the exact capitalization. +* #512: The client now has it's own `User-Agent`. +* #720: Some browser improvements. +* The zip release ships with [sabre/vobject 4.0.1][vobj], + [sabre/http 4.2.1][http], [sabre/event 3.0.0][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.3.0][xml]. + + +3.1.0-alpha2 (2015-09-05) +------------------------- + +* Massive calendars and addressbooks should see a big drop in peak memory + usage. +* Fixed a privilege bug in the availability system. +* #697: Added a "tableName" member to the PropertyStorage PDO backend. (@Frzk). +* #699: PostgreSQL fix for the Locks PDO backend. (@TCKnet) +* Removed the `simplefsserver.php` example file. It's not simple enough. +* #703: PropPatch in client is not correctly encoded. +* #709: Throw exception when running into empty + `supported-calendar-component-set`. +* #711: Don't trigger deserializers for empty elements in `{DAV:}prop`. This + fixes issues when using sabre/dav as a client. +* The zip release ships with [sabre/vobject 4.0.0-alpha2][vobj], + [sabre/http 4.1.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.2.0][xml]. + + +3.1.0-alpha1 (2015-07-19) +------------------------- + +* Now requires PHP 5.5 +* Upgraded to vobject 4, which is a lot faster. +* Support for PHP 7. +* #690: Support for `calendar-availability`, draft 05. + [reference][calendar-availability]. +* #691: Workaround for broken Windows Phone client. +* The zip release ships with [sabre/vobject 4.0.0-alpha1][vobj], + [sabre/http 4.0.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.1.0][xml]. + + +3.0.10 (2016-??-??) +------------------ + +* #889 Added support for filtering vCard properties in the addressbook-query + REPORT (@DeepDiver1975). + + +3.0.9 (2016-04-06) +------------------ + +* Set minimum libxml version to 2.7.0 in `composer.json`. +* #727: Added another workaround to make CalDAV work for Windows 10 clients. +* #805: It wasn't possible to create calendars that hold events, journals and + todos using MySQL, because the `components` column was 1 byte too small. +* The zip release ships with [sabre/vobject 3.5.1][vobj], + [sabre/http 4.2.1][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.1.0][uri] and [sabre/xml 1.4.1][xml]. + + +3.0.8 (2016-03-12) +------------------ + +* #784: Sync logs for address books were not correctly cleaned up after + deleting them. +* #787: Cannot use non-seekable stream-wrappers with range requests. +* Faster XML parsing and generating due to sabre/xml update. +* The zip release ships with [sabre/vobject 3.5.0][vobj], + [sabre/http 4.2.1][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.1.0][uri] and [sabre/xml 1.4.1][xml]. + + +3.0.7 (2016-01-12) +------------------ + +* #752: PHP 7 support for 3.0 branch. (@DeepDiver1975) +* The zip release ships with [sabre/vobject 3.5.0][vobj], + [sabre/http 4.2.1][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.3.0][xml]. + + +3.0.6 (2016-01-04) +------------------ + +* #730: Switched all mysql tables to `utf8mb4` character set, allowing you to + use emoji in some tables where you couldn't before. +* #729: Not all calls to `Sabre\DAV\Tree::getChildren()` were properly cached. +* #734: Return `418 I'm a Teapot` when generating a multistatus response that + has resources with no returned properties. +* #740: Bugs in `migrate20.php` script. +* The zip release ships with [sabre/vobject 3.4.8][vobj], + [sabre/http 4.1.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.3.0][xml]. + + +3.0.5 (2015-09-15) +------------------ + +* #704: Fixed broken uri encoding in multistatus responses. This affected + at least CyberDuck, but probably also others. +* The zip release ships with [sabre/vobject 3.4.7][vobj], +* The zip release ships with [sabre/vobject 3.4.7][vobj], + [sabre/http 4.1.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.2.0][xml]. + + +3.0.4 (2015-09-06) +------------------ + +* #703: PropPatch in client is not correctly encoded. +* #709: Throw exception when running into empty + `supported-calendar-component-set`. +* #711: Don't trigger deserializers for empty elements in `{DAV:}prop`. This + fixes issues when using sabre/dav as a client. +* #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 4.1.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.2.0][xml]. + + +3.0.3 (2015-08-06) +------------------ + +* #700: Digest Auth fails on `HEAD` requests. +* Fixed example files to no longer use now-deprecated realm argument. +* The zip release ships with [sabre/vobject 3.4.6][vobj], + [sabre/http 4.0.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.1.0][xml]. + + +3.0.2 (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. +* Fixed a whole bunch of incorrect php docblocks. +* The zip release ships with [sabre/vobject 3.4.5][vobj], + [sabre/http 4.0.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.1.0][xml]. + + +3.0.1 (2015-07-02) +------------------ + +* #674: Postgres sql file fixes. (@davesouthey) +* #677: Resources with the name '0' would not get retrieved when using + `Depth: infinity` in a `PROPFIND` request. +* #680: Fix 'autoprefixing' of dead `{DAV:}href` properties. +* #675: NTLM support in DAV\Client. (@k42b3) +* The zip release ships with [sabre/vobject 3.4.5][vobj], + [sabre/http 4.0.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.1.0][xml]. + + +3.0.0 (2015-06-02) +------------------ + +* No changes since last beta. +* The zip release ships with [sabre/vobject 3.4.5][vobj], + [sabre/http 4.0.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.0.0][xml]. + + +3.0.0-beta3 (2015-05-29) +------------------------ + +* Fixed deserializing href properties with no value. +* Fixed deserializing `{DAV:}propstat` without a `{DAV:}prop`. +* #668: More information about vcf-export-plugin in browser plugin. +* #669: Add export button to browser plugin for address books. (@mgee) +* #670: multiget report hrefs were not decoded. +* The zip release ships with [sabre/vobject 3.4.4][vobj], + [sabre/http 4.0.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.0.0][xml]. + + +3.0.0-beta2 (2015-05-27) +------------------------ + +* A node's properties should not overwrite properties that were already set. +* Some uris were not correctly encoded in notifications. +* The zip release ships with [sabre/vobject 3.4.4][vobj], + [sabre/http 4.0.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.0.0][xml]. + + +3.0.0-beta1 (2015-05-25) +------------------------ + +* `migrate22.php` is now called `migrate30.php`. +* Using php-cs-fixer for automated coding standards enforcement and fixing. +* #660: principals could break html output. +* #662: Fixed several bugs in the `share` request parser. +* #665: Fix a bug in serialization of complex properties in the proppatch + request in the client. +* #666: expand-property report did not correctly prepend the base uri when + generating uris, this caused delegation to break. +* #659: Don't throw errors when when etag-related checks are done on + collections. +* Fully supporting the updated `Prefer` header syntax, as defined in + [rfc7240][rfc7240]. +* The zip release ships with [sabre/vobject 3.4.3][vobj], + [sabre/http 4.0.0][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 1.0.0][xml]. + + +3.0.0-alpha1 (2015-05-19) +------------------------- + +* It's now possible to get all property information from files using the + browser plugin. +* Browser plugin will now show a 'calendar export' button when the + ics-export plugin is enabled. +* Some nodes that by default showed the current time as their last + modification time, now no longer has a last modification time. +* CardDAV namespace was missing from default namespaceMap. +* #646: Properties can now control their own HTML output in the browser plugin. +* #646: Nicer HTML output for the `{DAV:}acl` property. +* Browser plugin no longer shows a few properties that take up a lot of space, + but are likely not really interesting for most users. +* #654: Added a collection, `Sabre\DAVACL\FS\HomeCollection` for automatically + creating a private home collection per-user. +* Changed all MySQL columns from `VARCHAR` to `VARBINARY` where possible. +* Improved older migration scripts a bit to allow easier testing. +* The zip release ships with [sabre/vobject 3.4.3][vobj], + [sabre/http 4.0.0-alpha3][http], [sabre/event 2.0.2][evnt], + [sabre/uri 1.0.1][uri] and [sabre/xml 0.4.3][xml]. + + +2.2.0-alpha4 (2015-04-13) +------------------------- + +* Complete rewrite of the XML system. We now use our own [sabre/xml][xml], + which has a much smarter XML Reader and Writer. +* BC Break: It's no longer possible to instantiate the Locks plugin without + a locks backend. I'm not sure why this ever made sense. +* Simplified the Locking system and fixed a bug related to if tokens checking + locks unrelated to the current request. +* `FSExt` Directory and File no longer do custom property storage. This + functionality is already covered pretty well by the `PropertyStorage` plugin, + so please switch. +* Renamed `Sabre\CardDAV\UserAddressBooks` to `Sabre\CardDAV\AddressBookHome` + to be more consistent with `CalendarHome` as well as the CardDAV + specification. +* `Sabre\DAV\IExtendedCollection` now receives a `Sabre\DAV\MkCol` object as + its second argument, and no longer receives seperate properties and + resourcetype arguments. +* `MKCOL` now integrates better with propertystorage plugins. +* #623: Remove need of temporary files when working with Range requests. + (@dratini0) +* The zip release ships with [sabre/vobject 3.4.2][vobj], + [sabre/http 4.0.0-alpha1][http], [sabre/event 2.0.1][evnt], + [sabre/uri 1.0.0][uri] and [sabre/xml 0.4.3][xml]. + + +2.2.0-alpha3 (2015-02-25) +------------------------- + +* Contains all the changes introduced between 2.1.2 and 2.1.3. +* The zip release ships with [sabre/vobject 3.4.2][vobj], + [sabre/http 4.0.0-alpha1][http], [sabre/event 2.0.1][evnt] and + [sabre/uri 1.0.0][uri]. + + +2.2.0-alpha2 (2015-01-09) +------------------------- + +* Renamed `Sabre\DAV\Auth\Backend\BackendInterface::requireAuth` to + `challenge`, which is a more correct and better sounding name. +* The zip release ships with [sabre/vobject 3.3.5][vobj], + [sabre/http 3.0.4][http], [sabre/event 2.0.1][evnt]. + + +2.2.0-alpha1 (2014-12-10) +------------------------- + +* The browser plugin now has a new page with information about your sabredav + server, and shows information about every plugin that's loaded in the + system. +* #191: The Authentication system can now support multiple authentication + backends. +* Removed: all `$tableName` arguments from every PDO backend. This was already + deprecated, but has now been fully removed. All of these have been replaced + with public properties. +* Deleted several classes that were already deprecated much earlier: + * `Sabre\CalDAV\CalendarRootNode` + * `Sabre\CalDAV\UserCalendars` + * `Sabre\DAV\Exception\FileNotFound` + * `Sabre\DAV\Locks\Backend\FS` + * `Sabre\DAV\PartialUpdate\IFile` + * `Sabre\DAV\URLUtil` +* Removed: `Sabre\DAV\Client::addTrustedCertificates` and + `Sabre\DAV\Client::setVerifyPeer`. +* Removed: `Sabre\DAV\Plugin::getPlugin()` can now no longer return plugins + based on its class name. +* Removed: `Sabre\DAVACL\Plugin::getPrincipalByEmail()`. +* #560: GuessContentType plugin will now set content-type to + `application/octet-stream` if a better content-type could not be determined. +* #568: Added a `componentType` argument to `ICSExportPlugin`, allowing you to + specifically fetch `VEVENT`, `VTODO` or `VJOURNAL`. +* #582: Authentication backend interface changed to be stateless. If you + implemented your own authentication backend, make sure you upgrade your class + to the latest API! +* #582: `Sabre\DAV\Auth\Plugin::getCurrentUser()` is now deprecated. Use + `Sabre\DAV\Auth\Plugin::getCurrentPrincipal()` instead. +* #193: Fix `Sabre\DAV\FSExt\Directory::getQuotaInfo()` on windows. + + +2.1.11 (2016-10-06) +------------------- + +* #805: It wasn't possible to create calendars that hold events, journals and + todos using MySQL, because the `components` column was 1 byte too small. +* The zip release ships with [sabre/vobject 3.5.3][vobj], + [sabre/http 3.0.5][http], and [sabre/event 2.0.2][evnt]. + + +2.1.10 (2016-03-10) +------------------- + +* #784: Sync logs for address books were not correctly cleaned up after + deleting them. +* 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.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`. +* #572: `HEAD` requests trigger a PHP warning. +* #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 receive 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-existent + 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 flexibility. + + +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-existent + 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 compatible 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 compatibility 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: Adding 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 publicly 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_Aggregate, 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 DavFS2. + +Project started: 2007-12-13 + +[vobj]: http://sabre.io/vobject/ +[evnt]: http://sabre.io/event/ +[http]: http://sabre.io/http/ +[uri]: http://sabre.io/uri/ +[xml]: http://sabre.io/xml/ +[mi20]: http://sabre.io/dav/upgrade/1.8-to-2.0/ +[rfc6638]: http://tools.ietf.org/html/rfc6638 "CalDAV Scheduling" +[rfc7240]: http://tools.ietf.org/html/rfc7240 +[calendar-availability]: https://tools.ietf.org/html/draft-daboo-calendar-availability-05 diff --git a/htdocs/includes/sabre/sabre/dav/CONTRIBUTING.md b/htdocs/includes/sabre/sabre/dav/CONTRIBUTING.md index 425ee19ba85..b937db64f32 100644 --- a/htdocs/includes/sabre/sabre/dav/CONTRIBUTING.md +++ b/htdocs/includes/sabre/sabre/dav/CONTRIBUTING.md @@ -78,6 +78,28 @@ to accept the patch, but we'd still really like your contribution! To run the testsuite jump into the directory `cd tests` and trigger `phpunit`. Make sure you did a `composer install` beforehand. +Release process +--------------- + +Generally, these are the steps taken to do releases. + +1. Update the changelog. Every repo will have a `CHANGELOG.md` file. This file + should have a new version, and contain all the changes since the last + release. I generally run a `git diff` to figure out if I missed any changes. + This file should also have the current date. +2. If there were BC breaks, this usually now means a major version bump. +3. Ensure that `lib/Version.php` or `lib/DAV/Version.php` also matches this + version number. +4. Tag the release (Example `git tag 3.0.1` and push the tag (`git push --tags`). +5. (only for the sabre/dav project), create a zip distribution. Run + `php bin/build.php`. +6. For the relevant project, go to github and click the 'releases' tab. On this + tab I create the release with the relevant version. I also set the + description of the release to the same information of the changelog. In the + case of the `sabre/dav` project I also upload the zip distribution here. +7. Write a blog post on sabre.io. This also automatically updates twitter. + + [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/ diff --git a/htdocs/includes/sabre/sabre/dav/LICENSE b/htdocs/includes/sabre/sabre/dav/LICENSE new file mode 100644 index 00000000000..fd3539e33c6 --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/LICENSE @@ -0,0 +1,27 @@ +Copyright (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. diff --git a/htdocs/includes/sabre/sabre/dav/README.md b/htdocs/includes/sabre/sabre/dav/README.md new file mode 100644 index 00000000000..a06805443ae --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/README.md @@ -0,0 +1,38 @@ +![sabre's logo](http://sabre.io/img/logo.png) sabre/dav +======================================================= + +Introduction +------------ + +sabre/dav 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 | minimum PHP version | +| ------------ | ------ | ------------------- | +| master | [![Build Status](https://travis-ci.org/sabre-io/dav.svg?branch=master)](https://travis-ci.org/sabre-io/dav) | PHP 7.0 | +| 3.1 | [![Build Status](https://travis-ci.org/sabre-io/dav.svg?branch=3.0)](https://travis-ci.org/sabre-io/dav) | PHP 5.5 | +| 3.0 | [![Build Status](https://travis-ci.org/sabre-io/dav.svg?branch=3.0)](https://travis-ci.org/sabre-io/dav) | PHP 5.4 | +| 2.1 | [![Build Status](https://travis-ci.org/sabre-io/dav.svg?branch=2.1)](https://travis-ci.org/sabre-io/dav) | PHP 5.4 | +| 2.0 | [![Build Status](https://travis-ci.org/sabre-io/dav.svg?branch=2.0)](https://travis-ci.org/sabre-io/dav) | PHP 5.4 | +| 1.8 | [![Build Status](https://travis-ci.org/sabre-io/dav.svg?branch=1.8)](https://travis-ci.org/sabre-io/dav) | PHP 5.3 | +| 1.7 | [![Build Status](https://travis-ci.org/sabre-io/dav.svg?branch=1.7)](https://travis-ci.org/sabre-io/dav) | PHP 5.3 | +| 1.6 | [![Build Status](https://travis-ci.org/sabre-io/dav.svg?branch=1.6)](https://travis-ci.org/sabre-io/dav) | PHP 5.3 | + +Documentation +------------- + +* [Introduction](http://sabre.io/dav/). +* [Installation](http://sabre.io/dav/install/). + + +Made at fruux +------------- + +SabreDAV is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. diff --git a/htdocs/includes/sabre/sabre/dav/bin/build.php b/htdocs/includes/sabre/sabre/dav/bin/build.php old mode 100644 new mode 100755 index c4ba2094161..54174a777d9 --- a/htdocs/includes/sabre/sabre/dav/bin/build.php +++ b/htdocs/includes/sabre/sabre/dav/bin/build.php @@ -2,7 +2,6 @@ [ 'init', 'test', 'clean', ], @@ -10,25 +9,29 @@ $tasks = [ 'init', 'test', 'clean', ], 'clean' => [], - 'test' => [ + 'test' => [ 'composerupdate', ], - 'init' => [], + 'init' => [], 'composerupdate' => [], ]; $default = 'buildzip'; -$baseDir = __DIR__ . '/../'; +$baseDir = __DIR__.'/../'; chdir($baseDir); $currentTask = $default; -if ($argc > 1) $currentTask = $argv[1]; +if ($argc > 1) { + $currentTask = $argv[1]; +} $version = null; -if ($argc > 2) $version = $argv[2]; +if ($argc > 2) { + $version = $argv[2]; +} if (!isset($tasks[$currentTask])) { - echo "Task not found: ", $currentTask, "\n"; + echo 'Task not found: ', $currentTask, "\n"; die(1); } @@ -37,11 +40,9 @@ $newTaskList = []; $oldTaskList = [$currentTask => true]; while (count($oldTaskList) > 0) { - foreach ($oldTaskList as $task => $foo) { - if (!isset($tasks[$task])) { - echo "Dependency not found: " . $task, "\n"; + echo 'Dependency not found: '.$task, "\n"; die(1); } $dependencies = $tasks[$task]; @@ -55,82 +56,74 @@ while (count($oldTaskList) > 0) { $oldTaskList[$dependency] = true; $fullFilled = false; } - } if ($fullFilled) { unset($oldTaskList[$task]); $newTaskList[$task] = 1; } - } - } foreach (array_keys($newTaskList) as $task) { - - echo "task: " . $task, "\n"; + echo 'task: '.$task, "\n"; call_user_func($task); echo "\n"; - } -function init() { - +function init() +{ global $version; if (!$version) { - include __DIR__ . '/../vendor/autoload.php'; + include __DIR__.'/../vendor/autoload.php'; $version = Sabre\DAV\Version::VERSION; } - echo " Building sabre/dav " . $version, "\n"; - + echo ' Building sabre/dav '.$version, "\n"; } -function clean() { - +function clean() +{ global $baseDir; echo " Removing build files\n"; - $outputDir = $baseDir . '/build/SabreDAV'; + $outputDir = $baseDir.'/build/SabreDAV'; if (is_dir($outputDir)) { - system('rm -r ' . $baseDir . '/build/SabreDAV'); + system('rm -r '.$baseDir.'/build/SabreDAV'); } - } -function composerupdate() { - +function composerupdate() +{ global $baseDir; echo " Updating composer packages to latest version\n\n"; - system('cd ' . $baseDir . '; composer update'); + system('cd '.$baseDir.'; composer update'); } -function test() { - +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.dist --stop-on-failure', $code); - if ($code != 0) { + system(__DIR__.'/phpunit --configuration '.$baseDir.'/tests/phpunit.xml.dist --stop-on-failure', $code); + if (0 != $code) { echo "PHPUnit reported error code $code\n"; die(1); } - } -function buildzip() { - +function buildzip() +{ global $baseDir, $version; echo " Generating composer.json\n"; - $input = json_decode(file_get_contents(__DIR__ . '/../composer.json'), true); + $input = json_decode(file_get_contents(__DIR__.'/../composer.json'), true); $newComposer = [ - "require" => $input['require'], - "config" => [ - "bin-dir" => "./bin", + 'require' => $input['require'], + 'config' => [ + 'bin-dir' => './bin', ], - "prefer-stable" => true, - "minimum-stability" => "alpha", + 'prefer-stable' => true, + 'minimum-stability' => 'alpha', ]; unset( $newComposer['require']['sabre/vobject'], @@ -143,8 +136,8 @@ function buildzip() { file_put_contents('build/SabreDAV/composer.json', json_encode($newComposer, JSON_PRETTY_PRINT)); echo " Downloading dependencies\n"; - system("cd build/SabreDAV; composer install -n", $code); - if ($code !== 0) { + system('cd build/SabreDAV; composer install -n', $code); + if (0 !== $code) { echo "Composer reported error code $code\n"; die(1); } @@ -163,15 +156,14 @@ function buildzip() { ]; foreach ($fileNames as $fileName) { echo " $fileName\n"; - rename('build/SabreDAV/vendor/sabre/dav/' . $fileName, 'build/SabreDAV/' . $fileName); + 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."; + system('cd build; zip -qr sabredav-'.$version.'.zip SabreDAV'); + echo 'Done.'; } diff --git a/htdocs/includes/sabre/sabre/dav/bin/googlecode_upload.py b/htdocs/includes/sabre/sabre/dav/bin/googlecode_upload.py old mode 100644 new mode 100755 diff --git a/htdocs/includes/sabre/sabre/dav/bin/migrateto20.php b/htdocs/includes/sabre/sabre/dav/bin/migrateto20.php old mode 100644 new mode 100755 index 77236804f35..c7a8d9e350d --- a/htdocs/includes/sabre/sabre/dav/bin/migrateto20.php +++ b/htdocs/includes/sabre/sabre/dav/bin/migrateto20.php @@ -4,7 +4,6 @@ echo "SabreDAV migrate script for version 2.0\n"; if ($argc < 2) { - echo <<setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); @@ -66,23 +64,21 @@ $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); switch ($driver) { - - case 'mysql' : + case 'mysql': echo "Detected MySQL.\n"; break; - case 'sqlite' : + case 'sqlite': echo "Detected SQLite.\n"; break; - default : - echo "Error: unsupported driver: " . $driver . "\n"; + default: + echo 'Error: unsupported driver: '.$driver."\n"; die(-1); } foreach (['calendar', 'addressbook'] as $itemType) { - - $tableName = $itemType . 's'; - $tableNameOld = $tableName . '_old'; - $changesTable = $itemType . 'changes'; + $tableName = $itemType.'s'; + $tableNameOld = $tableName.'_old'; + $changesTable = $itemType.'changes'; echo "Upgrading '$tableName'\n"; @@ -90,18 +86,16 @@ foreach (['calendar', 'addressbook'] as $itemType) { $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' : + case 'mysql': $pdo->exec("RENAME TABLE $tableName TO $tableNameOld"); switch ($itemType) { - case 'calendar' : + case 'calendar': $pdo->exec(" CREATE TABLE calendars ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -119,7 +113,7 @@ foreach (['calendar', 'addressbook'] as $itemType) { ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; "); break; - case 'addressbook' : + case 'addressbook': $pdo->exec(" CREATE TABLE addressbooks ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -135,13 +129,13 @@ foreach (['calendar', 'addressbook'] as $itemType) { } break; - case 'sqlite' : + case 'sqlite': $pdo->exec("ALTER TABLE $tableName RENAME TO $tableNameOld"); switch ($itemType) { - case 'calendar' : - $pdo->exec(" + case 'calendar': + $pdo->exec(' CREATE TABLE calendars ( id integer primary key asc, principaluri text, @@ -155,10 +149,10 @@ foreach (['calendar', 'addressbook'] as $itemType) { components text, transparent bool ); - "); + '); break; - case 'addressbook' : - $pdo->exec(" + case 'addressbook': + $pdo->exec(' CREATE TABLE addressbooks ( id integer primary key asc, principaluri text, @@ -167,45 +161,37 @@ foreach (['calendar', 'addressbook'] as $itemType) { 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' : + 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' : + 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 { @@ -213,13 +199,11 @@ foreach (['calendar', 'addressbook'] as $itemType) { 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' : + case 'mysql': $pdo->exec(" CREATE TABLE $changesTable ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -232,7 +216,7 @@ foreach (['calendar', 'addressbook'] as $itemType) { "); break; - case 'sqlite' : + case 'sqlite': $pdo->exec(" CREATE TABLE $changesTable ( @@ -246,25 +230,20 @@ foreach (['calendar', 'addressbook'] as $itemType) { "); $pdo->exec("CREATE INDEX {$itemType}id_synctoken ON $changesTable ({$itemType}id, synctoken);"); break; - } - } - } try { - $pdo->query("SELECT * FROM calendarsubscriptions LIMIT 1"); + $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' : + case 'mysql': $pdo->exec(" CREATE TABLE calendarsubscriptions ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -283,8 +262,8 @@ CREATE TABLE calendarsubscriptions ( ); "); break; - case 'sqlite' : - $pdo->exec(" + case 'sqlite': + $pdo->exec(' CREATE TABLE calendarsubscriptions ( id integer primary key asc, @@ -300,64 +279,57 @@ CREATE TABLE calendarsubscriptions ( stripattachments bool, lastmodified int ); - "); + '); - $pdo->exec("CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);"); + $pdo->exec('CREATE INDEX principaluri_uri ON calendarsubscriptions (principaluri, uri);'); break; - } - } try { - $pdo->query("SELECT * FROM propertystorage LIMIT 1"); + $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(" + 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(" + '); + $pdo->exec(' CREATE UNIQUE INDEX path_property ON propertystorage (path(600), name(100)); - "); + '); break; - case 'sqlite' : - $pdo->exec(" + case 'sqlite': + $pdo->exec(' CREATE TABLE propertystorage ( id integer primary key asc, path TEXT, name TEXT, value TEXT ); - "); - $pdo->exec(" + '); + $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(); + $row = $pdo->query('SELECT * FROM cards LIMIT 1')->fetch(); if (!$row) { $random = mt_rand(1000, 9999); echo "There was no data in the cards table, so we're re-creating it\n"; @@ -366,28 +338,24 @@ try { $create = true; switch ($driver) { - case 'mysql' : + case 'mysql': $pdo->exec("RENAME TABLE cards TO cards_old$random"); break; - case 'sqlite' : + case 'sqlite': $pdo->exec("ALTER TABLE cards RENAME TO cards_old$random"); break; - } } - } catch (Exception $e) { - echo "Exception while checking cards table. Assuming that the table does not yet exist.\n"; - echo "Debug: ", $e->getMessage(), "\n"; + echo 'Debug: ', $e->getMessage(), "\n"; $create = true; - } if ($create) { switch ($driver) { - case 'mysql' : - $pdo->exec(" + case 'mysql': + $pdo->exec(' CREATE TABLE cards ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, addressbookid INT(11) UNSIGNED NOT NULL, @@ -398,12 +366,12 @@ CREATE TABLE cards ( size INT(11) UNSIGNED NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - "); + '); break; - case 'sqlite' : + case 'sqlite': - $pdo->exec(" + $pdo->exec(' CREATE TABLE cards ( id integer primary key asc, addressbookid integer, @@ -413,28 +381,26 @@ CREATE TABLE cards ( etag text, size integer ); - "); + '); break; - } } else { switch ($driver) { - case 'mysql' : - $pdo->exec(" + case 'mysql': + $pdo->exec(' ALTER TABLE cards ADD etag VARBINARY(32), ADD size INT(11) UNSIGNED NOT NULL; - "); + '); break; - case 'sqlite' : + case 'sqlite': - $pdo->exec(" + $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'); @@ -443,11 +409,9 @@ CREATE TABLE cards ( $stmt->execute([ md5($row['carddata']), strlen($row['carddata']), - $row['id'] + $row['id'], ]); } - - } echo "Upgrade to 2.0 schema completed.\n"; diff --git a/htdocs/includes/sabre/sabre/dav/bin/migrateto21.php b/htdocs/includes/sabre/sabre/dav/bin/migrateto21.php old mode 100644 new mode 100755 index c81ee5cca1a..90964358380 --- a/htdocs/includes/sabre/sabre/dav/bin/migrateto21.php +++ b/htdocs/includes/sabre/sabre/dav/bin/migrateto21.php @@ -4,7 +4,6 @@ echo "SabreDAV migrate script for version 2.1\n"; if ($argc < 2) { - echo <<setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); @@ -67,15 +65,14 @@ $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); switch ($driver) { - - case 'mysql' : + case 'mysql': echo "Detected MySQL.\n"; break; - case 'sqlite' : + case 'sqlite': echo "Detected SQLite.\n"; break; - default : - echo "Error: unsupported driver: " . $driver . "\n"; + default: + echo 'Error: unsupported driver: '.$driver."\n"; die(-1); } @@ -95,19 +92,17 @@ try { 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' : + case 'mysql': $pdo->exec('ALTER TABLE calendarobjects ADD uid VARCHAR(200)'); break; - case 'sqlite' : + case 'sqlite': $pdo->exec('ALTER TABLE calendarobjects ADD uid TEXT'); break; } @@ -117,7 +112,6 @@ if ($addUid) { $counter = 0; while ($row = $result->fetch(\PDO::FETCH_ASSOC)) { - try { $vobj = \Sabre\VObject\Reader::read($row['calendardata']); } catch (\Exception $e) { @@ -130,19 +124,16 @@ if ($addUid) { echo "Warning! Item with id $item[id] does NOT have a UID property and this is required.\n"; continue; } - $uid = (string)$item->UID; + $uid = (string) $item->UID; $stmt->execute([$uid, $row['id']]); - $counter++; - + ++$counter; } - } echo "Creating 'schedulingobjects'\n"; switch ($driver) { - - case 'mysql' : + case 'mysql': $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects ( id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -156,8 +147,7 @@ switch ($driver) { '); break; - - case 'sqlite' : + case 'sqlite': $pdo->exec('CREATE TABLE IF NOT EXISTS schedulingobjects ( id integer primary key asc, principaluri text, diff --git a/htdocs/includes/sabre/sabre/dav/bin/migrateto30.php b/htdocs/includes/sabre/sabre/dav/bin/migrateto30.php old mode 100644 new mode 100755 index 9ca77c13c3c..25e544c2ad2 --- a/htdocs/includes/sabre/sabre/dav/bin/migrateto30.php +++ b/htdocs/includes/sabre/sabre/dav/bin/migrateto30.php @@ -4,7 +4,6 @@ echo "SabreDAV migrate script for version 3.0\n"; if ($argc < 2) { - echo <<setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); @@ -66,15 +64,14 @@ $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); switch ($driver) { - - case 'mysql' : + case 'mysql': echo "Detected MySQL.\n"; break; - case 'sqlite' : + case 'sqlite': echo "Detected SQLite.\n"; break; - default : - echo "Error: unsupported driver: " . $driver . "\n"; + default: + echo 'Error: unsupported driver: '.$driver."\n"; die(-1); } @@ -90,9 +87,8 @@ try { echo "Renaming propertystorage -> propertystorage_old$random and creating new table.\n"; switch ($driver) { - - case 'mysql' : - $pdo->exec('RENAME TABLE propertystorage TO propertystorage_old' . $random); + case 'mysql': + $pdo->exec('RENAME TABLE propertystorage TO propertystorage_old'.$random); $pdo->exec(' CREATE TABLE propertystorage ( id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -102,10 +98,10 @@ try { value MEDIUMBLOB ); '); - $pdo->exec('CREATE UNIQUE INDEX path_property_' . $random . ' ON propertystorage (path(600), name(100));'); + $pdo->exec('CREATE UNIQUE INDEX path_property_'.$random.' ON propertystorage (path(600), name(100));'); break; - case 'sqlite' : - $pdo->exec('ALTER TABLE propertystorage RENAME TO propertystorage_old' . $random); + case 'sqlite': + $pdo->exec('ALTER TABLE propertystorage RENAME TO propertystorage_old'.$random); $pdo->exec(' CREATE TABLE propertystorage ( id integer primary key asc, @@ -115,9 +111,8 @@ CREATE TABLE propertystorage ( value blob );'); - $pdo->exec('CREATE UNIQUE INDEX path_property_' . $random . ' ON propertystorage (path, name);'); + $pdo->exec('CREATE UNIQUE INDEX path_property_'.$random.' ON propertystorage (path, name);'); break; - } } elseif (array_key_exists('valuetype', $row)) { echo "valuetype field exists. Assuming that this part of the migration has\n"; @@ -126,7 +121,6 @@ CREATE TABLE propertystorage ( echo "2.1 schema detected. Going to perform upgrade.\n"; $addValueType = true; } - } catch (Exception $e) { echo "Could not find a propertystorage table. Skipping this part of the\n"; echo "upgrade.\n"; @@ -134,19 +128,17 @@ CREATE TABLE propertystorage ( } if ($addValueType) { - switch ($driver) { - case 'mysql' : + case 'mysql': $pdo->exec('ALTER TABLE propertystorage ADD valuetype INT UNSIGNED'); break; - case 'sqlite' : + case 'sqlite': $pdo->exec('ALTER TABLE propertystorage ADD valuetype INT'); break; } $pdo->exec('UPDATE propertystorage SET valuetype = 1 WHERE valuetype IS NULL '); - } echo "Migrating vcardurl\n"; @@ -155,16 +147,14 @@ $result = $pdo->query('SELECT id, uri, vcardurl FROM principals WHERE vcardurl I $stmt1 = $pdo->prepare('INSERT INTO propertystorage (path, name, valuetype, value) VALUES (?, ?, 3, ?)'); while ($row = $result->fetch(\PDO::FETCH_ASSOC)) { - // Inserting the new record $stmt1->execute([ - 'addressbooks/' . basename($row['uri']), + 'addressbooks/'.basename($row['uri']), '{http://calendarserver.org/ns/}me-card', - serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl'])) + serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl'])), ]); echo serialize(new Sabre\DAV\Xml\Property\Href($row['vcardurl'])); - } echo "Done.\n"; diff --git a/htdocs/includes/sabre/sabre/dav/bin/migrateto32.php b/htdocs/includes/sabre/sabre/dav/bin/migrateto32.php old mode 100644 new mode 100755 index 7567aeb60f7..57fd3550761 --- a/htdocs/includes/sabre/sabre/dav/bin/migrateto32.php +++ b/htdocs/includes/sabre/sabre/dav/bin/migrateto32.php @@ -4,7 +4,6 @@ echo "SabreDAV migrate script for version 3.2\n"; if ($argc < 2) { - echo <<setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); @@ -69,15 +67,14 @@ $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); switch ($driver) { - - case 'mysql' : + case 'mysql': echo "Detected MySQL.\n"; break; - case 'sqlite' : + case 'sqlite': echo "Detected SQLite.\n"; break; - default : - echo "Error: unsupported driver: " . $driver . "\n"; + default: + echo 'Error: unsupported driver: '.$driver."\n"; die(-1); } @@ -91,7 +88,7 @@ try { echo "calendarinstances does not yet exist. Creating table and migrating data.\n"; switch ($driver) { - case 'mysql' : + case 'mysql': $pdo->exec(<<exec(" + $pdo->exec(' INSERT INTO calendarinstances ( calendarid, @@ -138,9 +135,9 @@ SELECT calendarcolor, transparent FROM calendars -"); +'); break; - case 'sqlite' : + case 'sqlite': $pdo->exec(<<exec(" + $pdo->exec(' INSERT INTO calendarinstances ( calendarid, @@ -187,10 +184,9 @@ SELECT calendarcolor, transparent FROM calendars -"); +'); break; } - } try { $result = $pdo->query('SELECT * FROM calendars LIMIT 1'); @@ -202,37 +198,34 @@ try { } $columnCount = count($row); - if ($columnCount === 3) { + if (3 === $columnCount) { echo "The calendars table has 3 columns already. Assuming this part of the migration was already done.\n"; $migrateCalendars = false; } else { - echo "The calendars table has " . $columnCount . " columns.\n"; + echo 'The calendars table has '.$columnCount." columns.\n"; $migrateCalendars = true; } - } catch (Exception $e) { echo "calendars table does not exist. This is a major problem. Exiting.\n"; exit(-1); } if ($migrateCalendars) { - - $calendarBackup = 'calendars_3_1_' . $backupPostfix; + $calendarBackup = 'calendars_3_1_'.$backupPostfix; echo "Backing up 'calendars' to '", $calendarBackup, "'\n"; switch ($driver) { - case 'mysql' : - $pdo->exec('RENAME TABLE calendars TO ' . $calendarBackup); + case 'mysql': + $pdo->exec('RENAME TABLE calendars TO '.$calendarBackup); break; - case 'sqlite' : - $pdo->exec('ALTER TABLE calendars RENAME TO ' . $calendarBackup); + case 'sqlite': + $pdo->exec('ALTER TABLE calendars RENAME TO '.$calendarBackup); break; - } echo "Creating new calendars table.\n"; switch ($driver) { - case 'mysql' : + case 'mysql': $pdo->exec(<<exec(<<stream = fopen('php://stdout', 'w'); - } - function log($msg) { - fwrite($this->stream, $msg . "\n"); + public 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"); +if ('cli-server' !== php_sapi_name()) { + die('This script is intended to run on the built-in php webserver'); } // Finding composer - $paths = [ - __DIR__ . '/../vendor/autoload.php', - __DIR__ . '/../../../autoload.php', + __DIR__.'/../vendor/autoload.php', + __DIR__.'/../../../autoload.php', ]; foreach ($paths as $path) { @@ -41,7 +39,7 @@ foreach ($paths as $path) { use Sabre\DAV; -// Root +// Root $root = new DAV\FS\Directory(getcwd()); // Setting up server. diff --git a/htdocs/includes/sabre/sabre/dav/composer.json b/htdocs/includes/sabre/sabre/dav/composer.json index 1fdeafad02c..c7e6184a32b 100644 --- a/htdocs/includes/sabre/sabre/dav/composer.json +++ b/htdocs/includes/sabre/sabre/dav/composer.json @@ -14,12 +14,12 @@ } ], "require": { - "php": ">=5.5.0", + "php": ">=7.0.0", "sabre/vobject": "^4.2.2", - "sabre/event" : ">=2.0.0, <4.0.0", - "sabre/xml" : "^1.4.0", - "sabre/http" : "^4.2.1", - "sabre/uri" : "^1.0.1", + "sabre/event" : "^5.0", + "sabre/xml" : "^2.2.5", + "sabre/http" : "^5.0.5", + "sabre/uri" : "^2.2.3", "ext-dom": "*", "ext-pcre": "*", "ext-spl": "*", @@ -29,17 +29,18 @@ "ext-date" : "*", "ext-iconv" : "*", "lib-libxml" : ">=2.7.0", - "psr/log": "^1.0" + "psr/log": "^1.0", + "ext-json": "*" }, "require-dev" : { - "phpunit/phpunit" : "> 4.8, <6.0.0", + "phpunit/phpunit" : "^6", "evert/phpdoc-md" : "~0.1.0", - "sabre/cs" : "^1.0.0", "monolog/monolog": "^1.18" }, "suggest" : { "ext-curl" : "*", - "ext-pdo" : "*" + "ext-pdo" : "*", + "ext-imap": "*" }, "autoload": { "psr-4" : { @@ -59,10 +60,5 @@ ], "config" : { "bin-dir" : "./bin" - }, - "extra" : { - "branch-alias": { - "dev-master": "3.1.0-dev" - } } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php index 311b1c41509..336e104c8ed 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php @@ -1,5 +1,7 @@ getCalendarObject($calendarId, $uri); }, $uris); - } /** @@ -103,23 +104,21 @@ abstract class AbstractBackend implements BackendInterface { * * @param mixed $calendarId * @param array $filters + * * @return array */ - function calendarQuery($calendarId, array $filters) { - + public function calendarQuery($calendarId, array $filters) + { $result = []; $objects = $this->getCalendarObjects($calendarId); foreach ($objects as $object) { - if ($this->validateFilterForObject($object, $filters)) { $result[] = $object['uri']; } - } return $result; - } /** @@ -128,10 +127,11 @@ abstract class AbstractBackend implements BackendInterface { * * @param array $object * @param array $filters + * * @return bool */ - protected function validateFilterForObject(array $object, array $filters) { - + 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. @@ -148,7 +148,6 @@ abstract class AbstractBackend implements BackendInterface { $vObject->destroy(); return $result; - } /** @@ -168,14 +167,14 @@ abstract class AbstractBackend implements BackendInterface { * * @param string $principalUri * @param string $uid + * * @return string|null */ - function getCalendarObjectByUID($principalUri, $uid) { - + public 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; @@ -189,38 +188,35 @@ abstract class AbstractBackend implements BackendInterface { $results = $this->calendarQuery( $calendar['id'], [ - 'name' => 'VCALENDAR', + 'name' => 'VCALENDAR', 'prop-filters' => [], 'comp-filters' => [ [ - 'name' => 'VEVENT', + 'name' => 'VEVENT', 'is-not-defined' => false, - 'time-range' => null, - 'comp-filters' => [], - 'prop-filters' => [ + 'time-range' => null, + 'comp-filters' => [], + 'prop-filters' => [ [ - 'name' => 'UID', + 'name' => 'UID', 'is-not-defined' => false, - 'time-range' => null, - 'text-match' => [ - 'value' => $uid, + 'time-range' => null, + 'text-match' => [ + 'value' => $uid, 'negate-condition' => false, - 'collation' => 'i;octet', + 'collation' => 'i;octet', ], 'param-filters' => [], ], - ] - ] + ], + ], ], ] ); if ($results) { // We have a match - return $calendar['uri'] . '/' . $results[0]; + return $calendar['uri'].'/'.$results[0]; } - } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/BackendInterface.php index bd8ee760228..7d125cf895c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/BackendInterface.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/BackendInterface.php @@ -1,5 +1,7 @@ 'displayname', + '{DAV:}displayname' => 'displayname', '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', - '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', - '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', - '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', + '{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', ]; /** @@ -109,24 +106,23 @@ class PDO extends AbstractBackend * @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', + '{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 + * Creates the backend. * * @param \PDO $pdo */ - function __construct(\PDO $pdo) { - + public function __construct(\PDO $pdo) + { $this->pdo = $pdo; - } /** @@ -151,10 +147,11 @@ class PDO extends AbstractBackend * ACL will automatically be put in read-only mode. * * @param string $principalUri + * * @return array */ - function getCalendarsForUser($principalUri) { - + public function getCalendarsForUser($principalUri) + { $fields = array_values($this->propertyMap); $fields[] = 'calendarid'; $fields[] = 'uri'; @@ -177,24 +174,23 @@ SQL $calendars = []; while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $components = []; if ($row['components']) { $components = explode(',', $row['components']); } $calendar = [ - 'id' => [(int)$row['calendarid'], (int)$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\Xml\Property\SupportedCalendarComponentSet($components), - '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'), - 'share-resource-uri' => '/ns/share/' . $row['calendarid'], + 'id' => [(int) $row['calendarid'], (int) $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\Xml\Property\SupportedCalendarComponentSet($components), + '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'), + 'share-resource-uri' => '/ns/share/'.$row['calendarid'], ]; - $calendar['share-access'] = (int)$row['access']; + $calendar['share-access'] = (int) $row['access']; // 1 = owner, 2 = readonly, 3 = readwrite if ($row['access'] > 1) { // We need to find more information about the original owner. @@ -203,7 +199,7 @@ SQL // read-only is for backwards compatbility. Might go away in // the future. - $calendar['read-only'] = (int)$row['access'] === \Sabre\DAV\Sharing\Plugin::ACCESS_READ; + $calendar['read-only'] = \Sabre\DAV\Sharing\Plugin::ACCESS_READ === (int) $row['access']; } foreach ($this->propertyMap as $xmlName => $dbName) { @@ -211,11 +207,9 @@ SQL } $calendars[] = $calendar; - } return $calendars; - } /** @@ -226,11 +220,12 @@ SQL * * @param string $principalUri * @param string $calendarUri - * @param array $properties + * @param array $properties + * * @return string */ - function createCalendar($principalUri, $calendarUri, array $properties) { - + public function createCalendar($principalUri, $calendarUri, array $properties) + { $fieldNames = [ 'principaluri', 'uri', @@ -239,51 +234,48 @@ SQL ]; $values = [ ':principaluri' => $principalUri, - ':uri' => $calendarUri, - ':transparent' => 0, + ':uri' => $calendarUri, + ':transparent' => 0, ]; - $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; if (!isset($properties[$sccs])) { // Default value $components = 'VEVENT,VTODO'; } else { if (!($properties[$sccs] instanceof CalDAV\Xml\Property\SupportedCalendarComponentSet)) { - throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet'); + throw new DAV\Exception('The '.$sccs.' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet'); } $components = implode(',', $properties[$sccs]->getValue()); } - $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; + $transp = '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp'; if (isset($properties[$transp])) { - $values[':transparent'] = $properties[$transp]->getValue() === 'transparent' ? 1 : 0; + $values[':transparent'] = 'transparent' === $properties[$transp]->getValue() ? 1 : 0; } - $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarTableName . " (synctoken, components) VALUES (1, ?)"); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarTableName.' (synctoken, components) VALUES (1, ?)'); $stmt->execute([$components]); $calendarId = $this->pdo->lastInsertId( - $this->calendarTableName . '_id_seq' + $this->calendarTableName.'_id_seq' ); $values[':calendarid'] = $calendarId; foreach ($this->propertyMap as $xmlName => $dbName) { if (isset($properties[$xmlName])) { - - $values[':' . $dbName] = $properties[$xmlName]; + $values[':'.$dbName] = $properties[$xmlName]; $fieldNames[] = $dbName; } } - $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarInstancesTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")"); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarInstancesTableName.' ('.implode(', ', $fieldNames).') VALUES ('.implode(', ', array_keys($values)).')'); $stmt->execute($values); return [ $calendarId, - $this->pdo->lastInsertId($this->calendarInstancesTableName . '_id_seq') + $this->pdo->lastInsertId($this->calendarInstancesTableName.'_id_seq'), ]; - } /** @@ -298,100 +290,88 @@ SQL * * Read the PropPatch documentation for more info and examples. * - * @param mixed $calendarId + * @param mixed $calendarId * @param \Sabre\DAV\PropPatch $propPatch - * @return void */ - function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) { - + public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } list($calendarId, $instanceId) = $calendarId; $supportedProperties = array_keys($this->propertyMap); - $supportedProperties[] = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; + $supportedProperties[] = '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp'; - $propPatch->handle($supportedProperties, function($mutations) use ($calendarId, $instanceId) { + $propPatch->handle($supportedProperties, function ($mutations) use ($calendarId, $instanceId) { $newValues = []; foreach ($mutations as $propertyName => $propertyValue) { - switch ($propertyName) { - case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' : + case '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp': $fieldName = 'transparent'; - $newValues[$fieldName] = $propertyValue->getValue() === 'transparent'; + $newValues[$fieldName] = 'transparent' === $propertyValue->getValue(); break; - default : + default: $fieldName = $this->propertyMap[$propertyName]; $newValues[$fieldName] = $propertyValue; break; } - } $valuesSql = []; foreach ($newValues as $fieldName => $value) { - $valuesSql[] = $fieldName . ' = ?'; + $valuesSql[] = $fieldName.' = ?'; } - $stmt = $this->pdo->prepare("UPDATE " . $this->calendarInstancesTableName . " SET " . implode(', ', $valuesSql) . " WHERE id = ?"); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarInstancesTableName.' SET '.implode(', ', $valuesSql).' WHERE id = ?'); $newValues['id'] = $instanceId; $stmt->execute(array_values($newValues)); - $this->addChange($calendarId, "", 2); + $this->addChange($calendarId, '', 2); return true; - }); - } /** - * Delete a calendar and all it's objects + * Delete a calendar and all it's objects. * * @param mixed $calendarId - * @return void */ - function deleteCalendar($calendarId) { - + public function deleteCalendar($calendarId) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } list($calendarId, $instanceId) = $calendarId; - $stmt = $this->pdo->prepare('SELECT access FROM ' . $this->calendarInstancesTableName . ' where id = ?'); + $stmt = $this->pdo->prepare('SELECT access FROM '.$this->calendarInstancesTableName.' where id = ?'); $stmt->execute([$instanceId]); - $access = (int)$stmt->fetchColumn(); - - if ($access === \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER) { + $access = (int) $stmt->fetchColumn(); + if (\Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER === $access) { /** * If the user is the owner of the calendar, we delete all data and all * instances. **/ - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?'); $stmt->execute([$calendarId]); - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarChangesTableName . ' WHERE calendarid = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarChangesTableName.' WHERE calendarid = ?'); $stmt->execute([$calendarId]); - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarInstancesTableName . ' WHERE calendarid = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE calendarid = ?'); $stmt->execute([$calendarId]); - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarTableName . ' WHERE id = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?'); $stmt->execute([$calendarId]); - } else { - /** * If it was an instance of a shared calendar, we only delete that * instance. */ - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarInstancesTableName . ' WHERE id = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE id = ?'); $stmt->execute([$instanceId]); - } - - } /** @@ -423,32 +403,32 @@ SQL * amount of times this is needed is reduced by a great degree. * * @param mixed $calendarId + * * @return array */ - function getCalendarObjects($calendarId) { - + public function getCalendarObjects($calendarId) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } list($calendarId, $instanceId) = $calendarId; - $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE 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' => (int)$row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'size' => (int)$row['size'], - 'component' => strtolower($row['componenttype']), + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => (int) $row['lastmodified'], + 'etag' => '"'.$row['etag'].'"', + 'size' => (int) $row['size'], + 'component' => strtolower($row['componenttype']), ]; } return $result; - } /** @@ -463,33 +443,35 @@ SQL * * This method must return null if the object did not exist. * - * @param mixed $calendarId + * @param mixed $calendarId * @param string $objectUri + * * @return array|null */ - function getCalendarObject($calendarId, $objectUri) { - + public function getCalendarObject($calendarId, $objectUri) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } list($calendarId, $instanceId) = $calendarId; - $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri = ?'); + $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; + if (!$row) { + return null; + } return [ - 'id' => $row['id'], - 'uri' => $row['uri'], - 'lastmodified' => (int)$row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'size' => (int)$row['size'], + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => (int) $row['lastmodified'], + 'etag' => '"'.$row['etag'].'"', + 'size' => (int) $row['size'], 'calendardata' => $row['calendardata'], - 'component' => strtolower($row['componenttype']), + 'component' => strtolower($row['componenttype']), ]; - } /** @@ -502,10 +484,11 @@ SQL * * @param mixed $calendarId * @param array $uris + * * @return array */ - function getMultipleCalendarObjects($calendarId, array $uris) { - + public function getMultipleCalendarObjects($calendarId, array $uris) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } @@ -513,7 +496,7 @@ SQL $result = []; foreach (array_chunk($uris, 900) as $chunk) { - $query = 'SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri IN ('; + $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($chunk), '?')); $query .= ')'; @@ -522,24 +505,21 @@ SQL $stmt->execute(array_merge([$calendarId], $chunk)); while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $result[] = [ - 'id' => $row['id'], - 'uri' => $row['uri'], - 'lastmodified' => (int)$row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'size' => (int)$row['size'], + 'id' => $row['id'], + 'uri' => $row['uri'], + 'lastmodified' => (int) $row['lastmodified'], + 'etag' => '"'.$row['etag'].'"', + 'size' => (int) $row['size'], 'calendardata' => $row['calendardata'], - 'component' => strtolower($row['componenttype']), + 'component' => strtolower($row['componenttype']), ]; - } } + return $result; - } - /** * Creates a new calendar object. * @@ -553,13 +533,14 @@ SQL * 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 mixed $calendarId * @param string $objectUri * @param string $calendarData + * * @return string|null */ - function createCalendarObject($calendarId, $objectUri, $calendarData) { - + public function createCalendarObject($calendarId, $objectUri, $calendarData) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } @@ -567,7 +548,7 @@ SQL $extraData = $this->getDenormalizedData($calendarData); - $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarObjectTableName . ' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)'); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)'); $stmt->execute([ $calendarId, $objectUri, @@ -582,8 +563,7 @@ SQL ]); $this->addChange($calendarId, $objectUri, 1); - return '"' . $extraData['etag'] . '"'; - + return '"'.$extraData['etag'].'"'; } /** @@ -599,13 +579,14 @@ SQL * 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 mixed $calendarId * @param string $objectUri * @param string $calendarData + * * @return string|null */ - function updateCalendarObject($calendarId, $objectUri, $calendarData) { - + public function updateCalendarObject($calendarId, $objectUri, $calendarData) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } @@ -613,13 +594,12 @@ SQL $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 = $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'] . '"'; - + return '"'.$extraData['etag'].'"'; } /** @@ -635,10 +615,11 @@ SQL * * uid - value of the UID property * * @param string $calendarData + * * @return array */ - protected function getDenormalizedData($calendarData) { - + protected function getDenormalizedData($calendarData) + { $vObject = VObject\Reader::read($calendarData); $componentType = null; $component = null; @@ -646,16 +627,16 @@ SQL $lastOccurence = null; $uid = null; foreach ($vObject->getComponents() as $component) { - if ($component->name !== 'VTIMEZONE') { + if ('VTIMEZONE' !== $component->name) { $componentType = $component->name; - $uid = (string)$component->UID; + $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') { + if ('VEVENT' === $componentType) { $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp(); // Finding the last occurence is a bit harder if (!isset($component->RRULE)) { @@ -673,7 +654,7 @@ SQL $lastOccurence = $firstOccurence; } } else { - $it = new VObject\Recur\EventIterator($vObject, (string)$component->UID); + $it = new VObject\Recur\EventIterator($vObject, (string) $component->UID); $maxDate = new \DateTime(self::MAX_DATE); if ($it->isInfinite()) { $lastOccurence = $maxDate->getTimeStamp(); @@ -682,30 +663,31 @@ SQL while ($it->valid() && $end < $maxDate) { $end = $it->getDtEnd(); $it->next(); - } $lastOccurence = $end->getTimeStamp(); } - } // Ensure Occurence values are positive - if ($firstOccurence < 0) $firstOccurence = 0; - if ($lastOccurence < 0) $lastOccurence = 0; + if ($firstOccurence < 0) { + $firstOccurence = 0; + } + if ($lastOccurence < 0) { + $lastOccurence = 0; + } } // Destroy circular references to PHP will GC the object. $vObject->destroy(); return [ - 'etag' => md5($calendarData), - 'size' => strlen($calendarData), - 'componentType' => $componentType, + 'etag' => md5($calendarData), + 'size' => strlen($calendarData), + 'componentType' => $componentType, 'firstOccurence' => $firstOccurence, - 'lastOccurence' => $lastOccurence, - 'uid' => $uid, + 'lastOccurence' => $lastOccurence, + 'uid' => $uid, ]; - } /** @@ -713,22 +695,20 @@ SQL * * The object uri is only the basename, or filename and not a full path. * - * @param mixed $calendarId + * @param mixed $calendarId * @param string $objectUri - * @return void */ - function deleteCalendarObject($calendarId, $objectUri) { - + public function deleteCalendarObject($calendarId, $objectUri) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } list($calendarId, $instanceId) = $calendarId; - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarId, $objectUri]); $this->addChange($calendarId, $objectUri, 3); - } /** @@ -781,10 +761,11 @@ SQL * * @param mixed $calendarId * @param array $filters + * * @return array */ - function calendarQuery($calendarId, array $filters) { - + public function calendarQuery($calendarId, array $filters) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } @@ -808,7 +789,7 @@ SQL $requirePostFilter = false; } // There was a time-range filter - if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) { + if ('VEVENT' == $componentType && 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 @@ -817,13 +798,12 @@ SQL $requirePostFilter = false; } } - } if ($requirePostFilter) { - $query = "SELECT uri, calendardata FROM " . $this->calendarObjectTableName . " WHERE calendarid = :calendarid"; + $query = 'SELECT uri, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = :calendarid'; } else { - $query = "SELECT uri FROM " . $this->calendarObjectTableName . " WHERE calendarid = :calendarid"; + $query = 'SELECT uri FROM '.$this->calendarObjectTableName.' WHERE calendarid = :calendarid'; } $values = [ @@ -831,16 +811,16 @@ SQL ]; if ($componentType) { - $query .= " AND componenttype = :componenttype"; + $query .= ' AND componenttype = :componenttype'; $values['componenttype'] = $componentType; } if ($timeRange && $timeRange['start']) { - $query .= " AND lastoccurence > :startdate"; + $query .= ' AND lastoccurence > :startdate'; $values['startdate'] = $timeRange['start']->getTimeStamp(); } if ($timeRange && $timeRange['end']) { - $query .= " AND firstoccurence < :enddate"; + $query .= ' AND firstoccurence < :enddate'; $values['enddate'] = $timeRange['end']->getTimeStamp(); } @@ -855,11 +835,9 @@ SQL } } $result[] = $row['uri']; - } return $result; - } /** @@ -879,10 +857,11 @@ SQL * * @param string $principalUri * @param string $uid + * * @return string|null */ - function getCalendarObjectByUID($principalUri, $uid) { - + public function getCalendarObjectByUID($principalUri, $uid) + { $query = <<execute([$principalUri, $uid]); if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return $row['calendaruri'] . '/' . $row['objecturi']; + return $row['calendaruri'].'/'.$row['objecturi']; } - } /** @@ -956,37 +934,41 @@ SQL; * * The limit is 'suggestive'. You are free to ignore it. * - * @param mixed $calendarId + * @param mixed $calendarId * @param string $syncToken - * @param int $syncLevel - * @param int $limit + * @param int $syncLevel + * @param int $limit + * * @return array */ - function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) { - + public function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } list($calendarId, $instanceId) = $calendarId; // Current synctoken - $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->calendarTableName . ' WHERE id = ?'); + $stmt = $this->pdo->prepare('SELECT synctoken FROM '.$this->calendarTableName.' WHERE id = ?'); $stmt->execute([$calendarId]); $currentToken = $stmt->fetchColumn(0); - if (is_null($currentToken)) return null; + if (is_null($currentToken)) { + return null; + } $result = [ 'syncToken' => $currentToken, - 'added' => [], - 'modified' => [], - 'deleted' => [], + '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; + $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); @@ -997,60 +979,54 @@ SQL; // 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 : + case 1: $result['added'][] = $uri; break; - case 2 : + case 2: $result['modified'][] = $uri; break; - case 3 : + case 3: $result['deleted'][] = $uri; break; } - } } else { // No synctoken supplied, this is the initial sync. - $query = "SELECT uri FROM " . $this->calendarObjectTableName . " WHERE calendarid = ?"; + $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; + return $result; } /** * Adds a change record to the calendarchanges table. * - * @param mixed $calendarId + * @param mixed $calendarId * @param string $objectUri - * @param int $operation 1 = add, 2 = modify, 3 = delete. - * @return void + * @param int $operation 1 = add, 2 = modify, 3 = delete */ - 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 = ?'); + 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 + $calendarId, ]); - $stmt = $this->pdo->prepare('UPDATE ' . $this->calendarTableName . ' SET synctoken = synctoken + 1 WHERE id = ?'); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET synctoken = synctoken + 1 WHERE id = ?'); $stmt->execute([ - $calendarId + $calendarId, ]); - } /** @@ -1082,10 +1058,11 @@ SQL; * default components). * * @param string $principalUri + * * @return array */ - function getSubscriptionsForUser($principalUri) { - + public function getSubscriptionsForUser($principalUri) + { $fields = array_values($this->subscriptionPropertyMap); $fields[] = 'id'; $fields[] = 'uri'; @@ -1095,20 +1072,19 @@ SQL; // 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 = $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'], + 'id' => $row['id'], + 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], - 'source' => $row['source'], + 'source' => $row['source'], 'lastmodified' => $row['lastmodified'], - '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']), + '{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']), ]; foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) { @@ -1118,11 +1094,9 @@ SQL; } $subscriptions[] = $subscription; - } return $subscriptions; - } /** @@ -1133,11 +1107,12 @@ SQL; * * @param string $principalUri * @param string $uri - * @param array $properties + * @param array $properties + * * @return mixed */ - function createSubscription($principalUri, $uri, array $properties) { - + public function createSubscription($principalUri, $uri, array $properties) + { $fieldNames = [ 'principaluri', 'uri', @@ -1151,30 +1126,28 @@ SQL; $values = [ ':principaluri' => $principalUri, - ':uri' => $uri, - ':source' => $properties['{http://calendarserver.org/ns/}source']->getHref(), + ':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]; + $values[':'.$dbName] = $properties[$xmlName]; $fieldNames[] = $dbName; } } - $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarSubscriptionsTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")"); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarSubscriptionsTableName.' ('.implode(', ', $fieldNames).') VALUES ('.implode(', ', array_keys($values)).')'); $stmt->execute($values); return $this->pdo->lastInsertId( - $this->calendarSubscriptionsTableName . '_id_seq' + $this->calendarSubscriptionsTableName.'_id_seq' ); - } /** - * Updates a subscription + * 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 @@ -1185,58 +1158,50 @@ SQL; * * Read the PropPatch documentation for more info and examples. * - * @param mixed $subscriptionId + * @param mixed $subscriptionId * @param \Sabre\DAV\PropPatch $propPatch - * @return void */ - function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) { - + public function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) + { $supportedProperties = array_keys($this->subscriptionPropertyMap); $supportedProperties[] = '{http://calendarserver.org/ns/}source'; - $propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) { - + $propPatch->handle($supportedProperties, function ($mutations) use ($subscriptionId) { $newValues = []; foreach ($mutations as $propertyName => $propertyValue) { - - if ($propertyName === '{http://calendarserver.org/ns/}source') { + if ('{http://calendarserver.org/ns/}source' === $propertyName) { $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 . ' = ?'; + $valuesSql[] = $fieldName.' = ?'; } - $stmt = $this->pdo->prepare("UPDATE " . $this->calendarSubscriptionsTableName . " SET " . implode(', ', $valuesSql) . ", lastmodified = ? WHERE id = ?"); + $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 + * Deletes a subscription. * * @param mixed $subscriptionId - * @return void */ - function deleteSubscription($subscriptionId) { - - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarSubscriptionsTableName . ' WHERE id = ?'); + public function deleteSubscription($subscriptionId) + { + $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarSubscriptionsTableName.' WHERE id = ?'); $stmt->execute([$subscriptionId]); - } /** @@ -1253,24 +1218,26 @@ SQL; * * @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 = ?'); + public 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; + if (!$row) { + return null; + } return [ - 'uri' => $row['uri'], + 'uri' => $row['uri'], 'calendardata' => $row['calendardata'], 'lastmodified' => $row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'size' => (int)$row['size'], + 'etag' => '"'.$row['etag'].'"', + 'size' => (int) $row['size'], ]; - } /** @@ -1282,40 +1249,38 @@ SQL; * 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 = ?'); + public 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'], + 'uri' => $row['uri'], 'lastmodified' => $row['lastmodified'], - 'etag' => '"' . $row['etag'] . '"', - 'size' => (int)$row['size'], + 'etag' => '"'.$row['etag'].'"', + 'size' => (int) $row['size'], ]; } return $result; - } /** - * Deletes a scheduling object + * 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 = ?'); + public function deleteSchedulingObject($principalUri, $objectUri) + { + $stmt = $this->pdo->prepare('DELETE FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ? AND uri = ?'); $stmt->execute([$principalUri, $objectUri]); - } /** @@ -1324,35 +1289,32 @@ SQL; * @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 (?, ?, ?, ?, ?, ?)'); + public 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)]); - } /** * Updates the list of shares. * - * @param mixed $calendarId + * @param mixed $calendarId * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees - * @return void */ - function updateInvites($calendarId, array $sharees) { - + public function updateInvites($calendarId, array $sharees) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); } $currentInvites = $this->getInvites($calendarId); list($calendarId, $instanceId) = $calendarId; - $removeStmt = $this->pdo->prepare("DELETE FROM " . $this->calendarInstancesTableName . " WHERE calendarid = ? AND share_href = ? AND access IN (2,3)"); - $updateStmt = $this->pdo->prepare("UPDATE " . $this->calendarInstancesTableName . " SET access = ?, share_displayname = ?, share_invitestatus = ? WHERE calendarid = ? AND share_href = ?"); + $removeStmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE calendarid = ? AND share_href = ? AND access IN (2,3)'); + $updateStmt = $this->pdo->prepare('UPDATE '.$this->calendarInstancesTableName.' SET access = ?, share_displayname = ?, share_invitestatus = ? WHERE calendarid = ? AND share_href = ?'); $insertStmt = $this->pdo->prepare(' -INSERT INTO ' . $this->calendarInstancesTableName . ' +INSERT INTO '.$this->calendarInstancesTableName.' ( calendarid, principaluri, @@ -1382,11 +1344,10 @@ INSERT INTO ' . $this->calendarInstancesTableName . ' ?, ?, ? - FROM ' . $this->calendarInstancesTableName . ' WHERE id = ?'); + FROM '.$this->calendarInstancesTableName.' WHERE id = ?'); foreach ($sharees as $sharee) { - - if ($sharee->access === \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS) { + if (\Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS === $sharee->access) { // if access was set no NOACCESS, it means access for an // existing sharee was removed. $removeStmt->execute([$calendarId, $sharee->href]); @@ -1404,7 +1365,6 @@ INSERT INTO ' . $this->calendarInstancesTableName . ' } foreach ($currentInvites as $oldSharee) { - if ($oldSharee->href === $sharee->href) { // This is an update $sharee->properties = array_merge( @@ -1416,11 +1376,10 @@ INSERT INTO ' . $this->calendarInstancesTableName . ' isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null, $sharee->inviteStatus ?: $oldSharee->inviteStatus, $calendarId, - $sharee->href + $sharee->href, ]); continue 2; } - } // If we got here, it means it was a new sharee $insertStmt->execute([ @@ -1431,11 +1390,9 @@ INSERT INTO ' . $this->calendarInstancesTableName . ' $sharee->href, isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null, $sharee->inviteStatus ?: \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE, - $instanceId + $instanceId, ]); - } - } /** @@ -1451,10 +1408,11 @@ INSERT INTO ' . $this->calendarInstancesTableName . ' * $properties * * @param mixed $calendarId + * * @return \Sabre\DAV\Xml\Element\Sharee[] */ - function getInvites($calendarId) { - + public function getInvites($calendarId) + { if (!is_array($calendarId)) { throw new \InvalidArgumentException('The value passed to getInvites() is expected to be an array with a calendarId and an instanceId'); } @@ -1477,35 +1435,29 @@ SQL; $result = []; while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $result[] = new Sharee([ - 'href' => isset($row['share_href']) ? $row['share_href'] : \Sabre\HTTP\encodePath($row['principaluri']), - 'access' => (int)$row['access'], + 'href' => isset($row['share_href']) ? $row['share_href'] : \Sabre\HTTP\encodePath($row['principaluri']), + 'access' => (int) $row['access'], /// Everyone is always immediately accepted, for now. - 'inviteStatus' => (int)$row['share_invitestatus'], - 'properties' => - !empty($row['share_displayname']) + 'inviteStatus' => (int) $row['share_invitestatus'], + 'properties' => !empty($row['share_displayname']) ? ['{DAV:}displayname' => $row['share_displayname']] : [], 'principal' => $row['principaluri'], ]); - } - return $result; + return $result; } /** - * Publishes a calendar + * Publishes a calendar. * * @param mixed $calendarId - * @param bool $value - * @return void + * @param bool $value */ - function setPublishStatus($calendarId, $value) { - + public function setPublishStatus($calendarId, $value) + { throw new DAV\Exception\NotImplemented('Not implemented'); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php index 6ec0bf06b9f..7986d8c33f1 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php @@ -1,5 +1,7 @@ pdo = $pdo; - } /** @@ -73,27 +74,25 @@ class SimplePDO extends AbstractBackend { * ACL will automatically be put in read-only mode. * * @param string $principalUri + * * @return array */ - function getCalendarsForUser($principalUri) { - + public function getCalendarsForUser($principalUri) + { // Making fields a comma-delimited list - $stmt = $this->pdo->prepare("SELECT id, uri FROM simple_calendars WHERE principaluri = ? ORDER BY id ASC"); + $stmt = $this->pdo->prepare('SELECT id, uri FROM simple_calendars WHERE principaluri = ? ORDER BY id ASC'); $stmt->execute([$principalUri]); $calendars = []; while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $calendars[] = [ - 'id' => $row['id'], - 'uri' => $row['uri'], + 'id' => $row['id'], + 'uri' => $row['uri'], 'principaluri' => $principalUri, ]; - } return $calendars; - } /** @@ -104,32 +103,30 @@ class SimplePDO extends AbstractBackend { * * @param string $principalUri * @param string $calendarUri - * @param array $properties + * @param array $properties + * * @return string */ - function createCalendar($principalUri, $calendarUri, array $properties) { - - $stmt = $this->pdo->prepare("INSERT INTO simple_calendars (principaluri, uri) VALUES (?, ?)"); + public function createCalendar($principalUri, $calendarUri, array $properties) + { + $stmt = $this->pdo->prepare('INSERT INTO simple_calendars (principaluri, uri) VALUES (?, ?)'); $stmt->execute([$principalUri, $calendarUri]); return $this->pdo->lastInsertId(); - } /** - * Delete a calendar and all it's objects + * Delete a calendar and all it's objects. * * @param string $calendarId - * @return void */ - function deleteCalendar($calendarId) { - + public function deleteCalendar($calendarId) + { $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ?'); $stmt->execute([$calendarId]); $stmt = $this->pdo->prepare('DELETE FROM simple_calendars WHERE id = ?'); $stmt->execute([$calendarId]); - } /** @@ -161,27 +158,27 @@ class SimplePDO extends AbstractBackend { * amount of times this is needed is reduced by a great degree. * * @param string $calendarId + * * @return array */ - function getCalendarObjects($calendarId) { - + public function getCalendarObjects($calendarId) + { $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ?'); $stmt->execute([$calendarId]); $result = []; foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { $result[] = [ - 'id' => $row['id'], - 'uri' => $row['uri'], - 'etag' => '"' . md5($row['calendardata']) . '"', - 'calendarid' => $calendarId, - 'size' => strlen($row['calendardata']), + 'id' => $row['id'], + 'uri' => $row['uri'], + 'etag' => '"'.md5($row['calendardata']).'"', + 'calendarid' => $calendarId, + 'size' => strlen($row['calendardata']), 'calendardata' => $row['calendardata'], ]; } return $result; - } /** @@ -198,25 +195,27 @@ class SimplePDO extends AbstractBackend { * * @param string $calendarId * @param string $objectUri + * * @return array|null */ - function getCalendarObject($calendarId, $objectUri) { - + public function getCalendarObject($calendarId, $objectUri) + { $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarId, $objectUri]); $row = $stmt->fetch(\PDO::FETCH_ASSOC); - if (!$row) return null; + if (!$row) { + return null; + } return [ - 'id' => $row['id'], - 'uri' => $row['uri'], - 'etag' => '"' . md5($row['calendardata']) . '"', - 'calendarid' => $calendarId, - 'size' => strlen($row['calendardata']), + 'id' => $row['id'], + 'uri' => $row['uri'], + 'etag' => '"'.md5($row['calendardata']).'"', + 'calendarid' => $calendarId, + 'size' => strlen($row['calendardata']), 'calendardata' => $row['calendardata'], ]; - } /** @@ -232,22 +231,22 @@ class SimplePDO extends AbstractBackend { * 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 mixed $calendarId * @param string $objectUri * @param string $calendarData + * * @return string|null */ - function createCalendarObject($calendarId, $objectUri, $calendarData) { - + public function createCalendarObject($calendarId, $objectUri, $calendarData) + { $stmt = $this->pdo->prepare('INSERT INTO simple_calendarobjects (calendarid, uri, calendardata) VALUES (?,?,?)'); $stmt->execute([ $calendarId, $objectUri, - $calendarData + $calendarData, ]); - return '"' . md5($calendarData) . '"'; - + return '"'.md5($calendarData).'"'; } /** @@ -263,18 +262,18 @@ class SimplePDO extends AbstractBackend { * 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 mixed $calendarId * @param string $objectUri * @param string $calendarData + * * @return string|null */ - function updateCalendarObject($calendarId, $objectUri, $calendarData) { - + public function updateCalendarObject($calendarId, $objectUri, $calendarData) + { $stmt = $this->pdo->prepare('UPDATE simple_calendarobjects SET calendardata = ? WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarData, $calendarId, $objectUri]); - return '"' . md5($calendarData) . '"'; - + return '"'.md5($calendarData).'"'; } /** @@ -284,13 +283,10 @@ class SimplePDO extends AbstractBackend { * * @param string $calendarId * @param string $objectUri - * @return void */ - function deleteCalendarObject($calendarId, $objectUri) { - + public function deleteCalendarObject($calendarId, $objectUri) + { $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarId, $objectUri]); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php index d77a2fe0fd5..2aaf95cdb73 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php @@ -1,5 +1,7 @@ caldavBackend = $caldavBackend; $this->calendarInfo = $calendarInfo; - } /** - * Returns the name of the calendar + * Returns the name of the calendar. * * @return string */ - function getName() { - + public function getName() + { return $this->calendarInfo['uri']; - } /** @@ -68,69 +68,68 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, * Read the PropPatch documentation for more information. * * @param PropPatch $propPatch - * @return void */ - function propPatch(PropPatch $propPatch) { - + public function propPatch(PropPatch $propPatch) + { return $this->caldavBackend->updateCalendar($this->calendarInfo['id'], $propPatch); - } /** - * Returns the list of properties + * Returns the list of properties. * * @param array $requestedProperties + * * @return array */ - function getProperties($requestedProperties) { - + public function getProperties($requestedProperties) + { $response = []; foreach ($this->calendarInfo as $propName => $propValue) { - - if (!is_null($propValue) && $propName[0] === '{') + if (!is_null($propValue) && '{' === $propName[0]) { $response[$propName] = $this->calendarInfo[$propName]; - + } } - return $response; + return $response; } /** - * Returns a calendar object + * 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) { - + public function getChild($name) + { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); - if (!$obj) throw new DAV\Exception\NotFound('Calendar object not found'); - + 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 + * Returns the full list of calendar objects. * * @return array */ - function getChildren() { - + public 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; + return $children; } /** @@ -140,77 +139,75 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, * If any children are not found, you do not have to return them. * * @param string[] $paths + * * @return array */ - function getMultipleChildren(array $paths) { - + public 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; + return $children; } /** * Checks if a child-node exists. * * @param string $name + * * @return bool */ - function childExists($name) { - + public function childExists($name) + { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); - if (!$obj) + if (!$obj) { return false; - else + } else { return true; - + } } /** - * Creates a new directory + * Creates a new directory. * * We actually block this, as subdirectories are not allowed in calendars. * * @param string $name - * @return void */ - function createDirectory($name) { - + public function createDirectory($name) + { throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed'); - } /** - * Creates a new file + * Creates a new file. * * The contents of the new file must be a valid ICalendar string. * - * @param string $name + * @param string $name * @param resource $calendarData + * * @return string|null */ - function createFile($name, $calendarData = null) { - + public function createFile($name, $calendarData = null) + { if (is_resource($calendarData)) { $calendarData = stream_get_contents($calendarData); } - return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'], $name, $calendarData); + return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'], $name, $calendarData); } /** * Deletes the calendar. - * - * @return void */ - function delete() { - + public function delete() + { $this->caldavBackend->deleteCalendar($this->calendarInfo['id']); - } /** @@ -218,36 +215,30 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, * {DAV:}displayname to display a name to display a name. * * @param string $newName - * @return void */ - function setName($newName) { - + public function setName($newName) + { throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported'); - } /** * Returns the last modification date as a unix timestamp. - * - * @return null */ - function getLastModified() { - + public function getLastModified() + { return null; - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->calendarInfo['principaluri']; - } /** @@ -262,8 +253,8 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, * * @return array */ - function getACL() { - + public function getACL() + { $acl = [ [ 'privilege' => '{DAV:}read', @@ -272,20 +263,19 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'principal' => $this->getOwner().'/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'principal' => $this->getOwner().'/calendar-proxy-read', 'protected' => true, ], [ - 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', + 'privilege' => '{'.Plugin::NS_CALDAV.'}read-free-busy', 'principal' => '{DAV:}authenticated', 'protected' => true, ], - ]; if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) { $acl[] = [ @@ -295,13 +285,12 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, ]; $acl[] = [ 'privilege' => '{DAV:}write', - 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'principal' => $this->getOwner().'/calendar-proxy-write', 'protected' => true, ]; } return $acl; - } /** @@ -311,8 +300,8 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, * * @return array */ - function getChildACL() { - + public function getChildACL() + { $acl = [ [ 'privilege' => '{DAV:}read', @@ -322,15 +311,14 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, [ 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'principal' => $this->getOwner().'/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'principal' => $this->getOwner().'/calendar-proxy-read', 'protected' => true, ], - ]; if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) { $acl[] = [ @@ -340,16 +328,14 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, ]; $acl[] = [ 'privilege' => '{DAV:}write', - 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'principal' => $this->getOwner().'/calendar-proxy-write', 'protected' => true, ]; - } + return $acl; - } - /** * Performs a calendar-query on the contents of this calendar. * @@ -365,12 +351,12 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, * documented by Sabre\CalDAV\CalendarQueryParser. * * @param array $filters + * * @return array */ - function calendarQuery(array $filters) { - + public function calendarQuery(array $filters) + { return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); - } /** @@ -382,8 +368,8 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, * * @return string|null */ - function getSyncToken() { - + public function getSyncToken() + { if ( $this->caldavBackend instanceof Backend\SyncSupport && isset($this->calendarInfo['{DAV:}sync-token']) @@ -396,7 +382,6 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, ) { return $this->calendarInfo['{http://sabredav.org/ns}sync-token']; } - } /** @@ -450,12 +435,13 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, * The limit is 'suggestive'. You are free to ignore it. * * @param string $syncToken - * @param int $syncLevel - * @param int $limit + * @param int $syncLevel + * @param int $limit + * * @return array */ - function getChanges($syncToken, $syncLevel, $limit = null) { - + public function getChanges($syncToken, $syncLevel, $limit = null) + { if (!$this->caldavBackend instanceof Backend\SyncSupport) { return null; } @@ -466,7 +452,5 @@ class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, $syncLevel, $limit ); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarHome.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarHome.php index ffd7f72fb69..663d449bf5c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarHome.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarHome.php @@ -1,12 +1,14 @@ caldavBackend = $caldavBackend; $this->principalInfo = $principalInfo; - } /** - * Returns the name of this object + * Returns the name of this object. * * @return string */ - function getName() { + public function getName() + { + list(, $name) = Uri\split($this->principalInfo['uri']); - list(, $name) = URLUtil::splitPath($this->principalInfo['uri']); return $name; - } /** - * Updates the name of this object + * Updates the name of this object. * * @param string $name - * @return void */ - function setName($name) { - + public function setName($name) + { throw new DAV\Exception\Forbidden(); - } /** - * Deletes this object - * - * @return void + * Deletes this object. */ - function delete() { - + public function delete() + { throw new DAV\Exception\Forbidden(); - } /** - * Returns the last modification date + * Returns the last modification date. * * @return int */ - function getLastModified() { - + public function getLastModified() + { return null; - } /** @@ -102,14 +97,12 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { * * This is currently not allowed * - * @param string $filename + * @param string $filename * @param resource $data - * @return void */ - function createFile($filename, $data = null) { - + public function createFile($filename, $data = null) + { throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); - } /** @@ -118,30 +111,29 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { * This is currently not allowed. * * @param string $filename - * @return void */ - function createDirectory($filename) { - + public function createDirectory($filename) + { throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); - } /** - * Returns a single calendar, by name + * Returns a single calendar, by name. * * @param string $name + * * @return Calendar */ - function getChild($name) { - + public function getChild($name) + { // Special nodes - if ($name === 'inbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) { + if ('inbox' === $name && $this->caldavBackend instanceof Backend\SchedulingSupport) { return new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']); } - if ($name === 'outbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) { + if ('outbox' === $name && $this->caldavBackend instanceof Backend\SchedulingSupport) { return new Schedule\Outbox($this->principalInfo['uri']); } - if ($name === 'notifications' && $this->caldavBackend instanceof Backend\NotificationSupport) { + if ('notifications' === $name && $this->caldavBackend instanceof Backend\NotificationSupport) { return new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); } @@ -162,36 +154,34 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { return new Subscriptions\Subscription($this->caldavBackend, $subscription); } } - } - throw new NotFound('Node with name \'' . $name . '\' could not be found'); - + throw new NotFound('Node with name \''.$name.'\' could not be found'); } /** * Checks if a calendar exists. * * @param string $name + * * @return bool */ - function childExists($name) { - + public function childExists($name) + { try { - return !!$this->getChild($name); + return (bool) $this->getChild($name); } catch (NotFound $e) { return false; } - } /** - * Returns a list of calendars + * Returns a list of calendars. * * @return array */ - function getChildren() { - + public function getChildren() + { $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); $objs = []; foreach ($calendars as $calendar) { @@ -220,35 +210,34 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { } return $objs; - } /** * Creates a new calendar or subscription. * * @param string $name - * @param MkCol $mkCol + * @param MkCol $mkCol + * * @throws DAV\Exception\InvalidResourceType - * @return void */ - function createExtendedCollection($name, MkCol $mkCol) { - + public function createExtendedCollection($name, MkCol $mkCol) + { $isCalendar = false; $isSubscription = false; foreach ($mkCol->getResourceType() as $rt) { switch ($rt) { - case '{DAV:}collection' : - case '{http://calendarserver.org/ns/}shared-owner' : + case '{DAV:}collection': + case '{http://calendarserver.org/ns/}shared-owner': // ignore break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : + case '{urn:ietf:params:xml:ns:caldav}calendar': $isCalendar = true; break; - case '{http://calendarserver.org/ns/}subscribed' : + case '{http://calendarserver.org/ns/}subscribed': $isSubscription = true; break; - default : - throw new DAV\Exception\InvalidResourceType('Unknown resourceType: ' . $rt); + default: + throw new DAV\Exception\InvalidResourceType('Unknown resourceType: '.$rt); } } @@ -260,15 +249,11 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { 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'); - } - } /** @@ -276,10 +261,9 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { * * @return string */ - function getOwner() { - + public function getOwner() + { return $this->principalInfo['uri']; - } /** @@ -294,8 +278,8 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { * * @return array */ - function getACL() { - + public function getACL() + { return [ [ 'privilege' => '{DAV:}read', @@ -309,46 +293,43 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'principal' => $this->principalInfo['uri'].'/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}write', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', + 'principal' => $this->principalInfo['uri'].'/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', + 'principal' => $this->principalInfo['uri'].'/calendar-proxy-read', 'protected' => true, ], - ]; - } - /** * 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 $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 + * @param string $inReplyTo The unique id this message is a response to + * @param string $summary A description of the reply + * + * @return string|null */ - function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { - + public 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); - } /** @@ -367,12 +348,11 @@ class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { * collection should be ignored. * * @param string $uid + * * @return string|null */ - function getCalendarObjectByUID($uid) { - + public function getCalendarObjectByUID($uid) + { return $this->caldavBackend->getCalendarObjectByUID($this->principalInfo['uri'], $uid); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarObject.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarObject.php index 9d6532a35b1..3f28ee7f7d2 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarObject.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarObject.php @@ -1,5 +1,7 @@ caldavBackend = $caldavBackend; if (!isset($objectData['uri'])) { @@ -62,44 +64,43 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\ $this->calendarInfo = $calendarInfo; $this->objectData = $objectData; - } /** - * Returns the uri for this object + * Returns the uri for this object. * * @return string */ - function getName() { - + public function getName() + { return $this->objectData['uri']; - } /** - * Returns the ICalendar-formatted object + * Returns the ICalendar-formatted object. * * @return string */ - function get() { - + public function get() + { // Pre-populating the 'calendardata' is optional, if we don't have it // already we fetch it from the backend. if (!isset($this->objectData['calendardata'])) { $this->objectData = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $this->objectData['uri']); } - return $this->objectData['calendardata']; + return $this->objectData['calendardata']; } /** - * Updates the ICalendar-formatted object + * Updates the ICalendar-formatted object. * * @param string|resource $calendarData + * * @return string */ - function put($calendarData) { - + public function put($calendarData) + { if (is_resource($calendarData)) { $calendarData = stream_get_contents($calendarData); } @@ -108,33 +109,29 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\ $this->objectData['etag'] = $etag; return $etag; - } /** - * Deletes the calendar object - * - * @return void + * Deletes the calendar object. */ - function delete() { - + public function delete() + { $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'], $this->objectData['uri']); - } /** - * Returns the mime content-type + * Returns the mime content-type. * * @return string */ - function getContentType() { - + public function getContentType() + { $mime = 'text/calendar; charset=utf-8'; if (isset($this->objectData['component']) && $this->objectData['component']) { - $mime .= '; component=' . $this->objectData['component']; + $mime .= '; component='.$this->objectData['component']; } - return $mime; + return $mime; } /** @@ -144,53 +141,49 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\ * * @return string */ - function getETag() { - + public function getETag() + { if (isset($this->objectData['etag'])) { return $this->objectData['etag']; } else { - return '"' . md5($this->get()) . '"'; + return '"'.md5($this->get()).'"'; } - } /** - * Returns the last modification date as a unix timestamp + * Returns the last modification date as a unix timestamp. * * @return int */ - function getLastModified() { - + public function getLastModified() + { return $this->objectData['lastmodified']; - } /** - * Returns the size of this object in bytes + * Returns the size of this object in bytes. * * @return int */ - function getSize() { - + public function getSize() + { if (array_key_exists('size', $this->objectData)) { return $this->objectData['size']; } else { return strlen($this->get()); } - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->calendarInfo['principaluri']; - } /** @@ -205,8 +198,8 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\ * * @return array */ - function getACL() { - + public function getACL() + { // An alternative acl may be specified in the object data. if (isset($this->objectData['acl'])) { return $this->objectData['acl']; @@ -221,17 +214,14 @@ class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\ ], [ 'privilege' => '{DAV:}all', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-read', 'protected' => true, ], - ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarQueryValidator.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarQueryValidator.php index df8008fe276..0e7f1307d1a 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarQueryValidator.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarQueryValidator.php @@ -1,12 +1,14 @@ name !== $filters['name']) { @@ -40,8 +43,6 @@ class CalendarQueryValidator { return $this->validateCompFilters($vObject, $filters['comp-filters']) && $this->validatePropFilters($vObject, $filters['prop-filters']); - - } /** @@ -52,23 +53,21 @@ class CalendarQueryValidator { * itself. * * @param VObject\Component $parent - * @param array $filters + * @param array $filters + * * @return bool */ - protected function validateCompFilters(VObject\Component $parent, array $filters) { - + 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; @@ -80,6 +79,7 @@ class CalendarQueryValidator { continue 2; } } + return false; } @@ -90,27 +90,23 @@ class CalendarQueryValidator { // 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; + // 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; - } /** @@ -121,23 +117,21 @@ class CalendarQueryValidator { * itself. * * @param VObject\Component $parent - * @param array $filters + * @param array $filters + * * @return bool */ - protected function validatePropFilters(VObject\Component $parent, array $filters) { - + 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; @@ -149,6 +143,7 @@ class CalendarQueryValidator { continue 2; } } + return false; } @@ -159,7 +154,6 @@ class CalendarQueryValidator { // 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'])) @@ -167,20 +161,17 @@ class CalendarQueryValidator { // 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; - } /** @@ -191,23 +182,21 @@ class CalendarQueryValidator { * itself. * * @param VObject\Property $parent - * @param array $filters + * @param array $filters + * * @return bool */ - protected function validateParamFilters(VObject\Property $parent, array $filters) { - + 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; @@ -220,24 +209,20 @@ class CalendarQueryValidator { // 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; - } /** @@ -246,20 +231,20 @@ class CalendarQueryValidator { * 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 + * @param VObject\Node|string $check value to check against + * @param array $textMatch + * * @return bool */ - protected function validateTextMatch($check, array $textMatch) { - + 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); - + return $textMatch['negate-condition'] xor $isMatching; } /** @@ -269,12 +254,13 @@ class CalendarQueryValidator { * complex. * * @param VObject\Node $component - * @param DateTime $start - * @param DateTime $end + * @param DateTime $start + * @param DateTime $end + * * @return bool */ - protected function validateTimeRange(VObject\Node $component, $start, $end) { - + protected function validateTimeRange(VObject\Node $component, $start, $end) + { if (is_null($start)) { $start = new DateTime('1900-01-01'); } @@ -283,14 +269,13 @@ class CalendarQueryValidator { } switch ($component->name) { - - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : + case 'VEVENT': + case 'VTODO': + case 'VJOURNAL': return $component->isInTimeRange($start, $end); - case 'VALARM' : + case 'VALARM': // If the valarm is wrapped in a recurring event, we need to // expand the recursions, and validate each. @@ -298,10 +283,9 @@ class CalendarQueryValidator { // 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) { - + if ('VEVENT' === $component->parent->name && $component->parent->RRULE) { // Fire up the iterator! - $it = new VObject\Recur\EventIterator($component->parent->parent, (string)$component->parent->UID); + $it = new VObject\Recur\EventIterator($component->parent->parent, (string) $component->parent->UID); while ($it->valid()) { $expandedEvent = $it->getEventObject(); @@ -309,15 +293,14 @@ class CalendarQueryValidator { // 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) { + if (null !== $expandedEvent->VALARM) { foreach ($expandedEvent->VALARM as $expandedAlarm) { - $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); if ($expandedAlarm->isInTimeRange($start, $end)) { return true; } - if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') { + if ('DATE-TIME' === (string) $expandedAlarm->TRIGGER['VALUE']) { // This is an alarm with a non-relative trigger // time, likely created by a buggy client. The // implication is that every alarm in this @@ -346,30 +329,26 @@ class CalendarQueryValidator { } $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'); + // no break + 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'); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarRoot.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarRoot.php index 1d6b2ac9ff8..42f1a44d577 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarRoot.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarRoot.php @@ -1,11 +1,13 @@ caldavBackend = $caldavBackend; - } /** - * Returns the nodename + * 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() { - + public function getName() + { return Plugin::CALENDAR_ROOT; - } /** @@ -69,12 +69,11 @@ class CalendarRoot extends \Sabre\DAVACL\AbstractPrincipalCollection { * supplied by the authentication backend. * * @param array $principal + * * @return \Sabre\DAV\INode */ - function getChildForPrincipal(array $principal) { - + public function getChildForPrincipal(array $principal) + { return new CalendarHome($this->caldavBackend, $principal); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php index 7aff2edab55..3385ad8fe85 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Exception/InvalidComponentType.php @@ -1,35 +1,34 @@ ownerDocument; $np = $doc->createElementNS(CalDAV\Plugin::NS_CALDAV, 'cal:supported-calendar-component'); $errorNode->appendChild($np); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICSExportPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICSExportPlugin.php index fc8b971f3b4..717d3a9c92c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICSExportPlugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICSExportPlugin.php @@ -1,5 +1,7 @@ server = $server; $server->on('method:GET', [$this, 'httpGet'], 90); - $server->on('browserButtonActions', function($path, $node, &$actions) { + $server->on('browserButtonActions', function ($path, $node, &$actions) { if ($node instanceof ICalendar) { - $actions .= ''; + $actions .= ''; } }); - } /** * Intercepts GET requests on calendar urls ending with ?export. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * + * @throws BadRequest + * @throws DAV\Exception\NotFound + * @throws VObject\InvalidDataException + * * @return bool */ - function httpGet(RequestInterface $request, ResponseInterface $response) { - + public function httpGet(RequestInterface $request, ResponseInterface $response) + { $queryParams = $request->getQueryParameters(); - if (!array_key_exists('export', $queryParams)) return; + if (!array_key_exists('export', $queryParams)) { + return; + } $path = $request->getPath(); @@ -93,7 +100,7 @@ class ICSExportPlugin extends DAV\ServerPlugin { '{http://apple.com/ns/ical/}calendar-color', ]); - if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{' . Plugin::NS_CALDAV . '}calendar')) { + if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{'.Plugin::NS_CALDAV.'}calendar')) { return; } // Marking the transactionType, for logging purposes. @@ -117,7 +124,7 @@ class ICSExportPlugin extends DAV\ServerPlugin { } $end = DateTime::createFromFormat('U', $queryParams['end']); } - if (isset($queryParams['expand']) && !!$queryParams['expand']) { + if (isset($queryParams['expand']) && (bool) $queryParams['expand']) { if (!$start || !$end) { throw new BadRequest('If you\'d like to expand recurrences, you must specify both a start= and end= parameter.'); } @@ -126,12 +133,12 @@ class ICSExportPlugin extends DAV\ServerPlugin { } if (isset($queryParams['componentType'])) { if (!in_array($queryParams['componentType'], ['VEVENT', 'VTODO', 'VJOURNAL'])) { - throw new BadRequest('You are not allowed to search for components of type: ' . $queryParams['componentType'] . ' here'); + throw new BadRequest('You are not allowed to search for components of type: '.$queryParams['componentType'].' here'); } $componentType = $queryParams['componentType']; } - $format = \Sabre\HTTP\Util::Negotiate( + $format = \Sabre\HTTP\negotiateContentType( $request->getHeader('Accept'), [ 'text/calendar', @@ -140,7 +147,7 @@ class ICSExportPlugin extends DAV\ServerPlugin { ); if (isset($queryParams['accept'])) { - if ($queryParams['accept'] === 'application/calendar+json' || $queryParams['accept'] === 'jcal') { + if ('application/calendar+json' === $queryParams['accept'] || 'jcal' === $queryParams['accept']) { $format = 'application/calendar+json'; } } @@ -152,61 +159,61 @@ class ICSExportPlugin extends DAV\ServerPlugin { // 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 $componentType - * @param string $format - * @param array $properties + * @param string $path + * @param DateTime|null $start + * @param DateTime|null $end + * @param bool $expand + * @param string $componentType + * @param string $format + * @param array $properties * @param ResponseInterface $response + * + * @throws DAV\Exception\NotFound + * @throws VObject\InvalidDataException */ - protected function generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response) { - - $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data'; + protected function generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response) + { + $calDataProp = '{'.Plugin::NS_CALDAV.'}calendar-data'; $calendarNode = $this->server->tree->getNodeForPath($path); $blobs = []; if ($start || $end || $componentType) { - // If there was a start or end filter, we need to enlist // calendarQuery for speed. $queryResult = $calendarNode->calendarQuery([ - 'name' => 'VCALENDAR', + 'name' => 'VCALENDAR', 'comp-filters' => [ [ - 'name' => $componentType, - 'comp-filters' => [], - 'prop-filters' => [], + 'name' => $componentType, + 'comp-filters' => [], + 'prop-filters' => [], 'is-not-defined' => false, - 'time-range' => [ + 'time-range' => [ 'start' => $start, - 'end' => $end, + 'end' => $end, ], ], ], - 'prop-filters' => [], + 'prop-filters' => [], 'is-not-defined' => false, - 'time-range' => null, + '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; + function ($item) use ($path) { + return $path.'/'.$item; }, $queryResult ); $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]); unset($queryResult); - } else { $nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1); } @@ -228,7 +235,7 @@ class ICSExportPlugin extends DAV\ServerPlugin { $calendarTimeZone = null; // We're expanding, and for that we need to figure out the // calendar's timezone. - $tzProp = '{' . Plugin::NS_CALDAV . '}calendar-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 @@ -249,11 +256,11 @@ class ICSExportPlugin extends DAV\ServerPlugin { $filenameExtension = '.ics'; switch ($format) { - case 'text/calendar' : + case 'text/calendar': $mergedCalendar = $mergedCalendar->serialize(); $filenameExtension = '.ics'; break; - case 'application/calendar+json' : + case 'application/calendar+json': $mergedCalendar = json_encode($mergedCalendar->jsonSerialize()); $filenameExtension = '.json'; break; @@ -264,29 +271,29 @@ class ICSExportPlugin extends DAV\ServerPlugin { '', $calendarNode->getName() ); - $filename .= '-' . date('Y-m-d') . $filenameExtension; + $filename .= '-'.date('Y-m-d').$filenameExtension; - $response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); + $response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"'); $response->setHeader('Content-Type', $format); $response->setStatus(200); $response->setBody($mergedCalendar); - } /** * Merges all calendar objects, and builds one big iCalendar blob. * - * @param array $properties Some CalDAV properties + * @param array $properties Some CalDAV properties * @param array $inputObjects + * * @return VObject\Component\VCalendar */ - function mergeObjects(array $properties, array $inputObjects) { - + public 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'; + $calendar->PRODID = '-//SabreDAV//SabreDAV '.DAV\Version::VERSION.'//EN'; } else { $calendar->PRODID = '-//SabreDAV//SabreDAV//EN'; } @@ -303,41 +310,41 @@ class ICSExportPlugin extends DAV\ServerPlugin { $objects = []; foreach ($inputObjects as $href => $inputObject) { - $nodeComp = VObject\Reader::read($inputObject); foreach ($nodeComp->children() as $child) { - switch ($child->name) { - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : + case 'VEVENT': + case 'VTODO': + case 'VJOURNAL': $objects[] = clone $child; break; // VTIMEZONE is special, because we need to filter out the duplicates - case 'VTIMEZONE' : + case 'VTIMEZONE': // Naively just checking tzid. - if (in_array((string)$child->TZID, $collectedTimezones)) continue; + if (in_array((string) $child->TZID, $collectedTimezones)) { + break; + } $timezones[] = clone $child; $collectedTimezones[] = $child->TZID; break; - } - } // Destroy circular references to PHP will GC the object. $nodeComp->destroy(); unset($nodeComp); - } - foreach ($timezones as $tz) $calendar->add($tz); - foreach ($objects as $obj) $calendar->add($obj); + foreach ($timezones as $tz) { + $calendar->add($tz); + } + foreach ($objects as $obj) { + $calendar->add($obj); + } return $calendar; - } /** @@ -348,10 +355,9 @@ class ICSExportPlugin extends DAV\ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'ics-export'; - } /** @@ -365,14 +371,12 @@ class ICSExportPlugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds the ability to export CalDAV calendars as a single iCalendar file.', - 'link' => 'http://sabre.io/dav/ics-export-plugin/', + 'link' => 'http://sabre.io/dav/ics-export-plugin/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICalendar.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICalendar.php index 7cf4b12561a..8636e0baeff 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICalendar.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICalendar.php @@ -1,11 +1,13 @@ caldavBackend = $caldavBackend; $this->principalUri = $principalUri; - } /** - * Returns all notifications for a principal + * Returns all notifications for a principal. * * @return array */ - function getChildren() { - + public function getChildren() + { $children = []; $notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri); foreach ($notifications as $notification) { - $children[] = new Node( $this->caldavBackend, $this->principalUri, @@ -71,31 +71,27 @@ class Collection extends DAV\Collection implements ICollection, DAVACL\IACL { } return $children; - } /** - * Returns the name of this object + * Returns the name of this object. * * @return string */ - function getName() { - + public function getName() + { return 'notifications'; - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->principalUri; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/ICollection.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/ICollection.php index 008e87435a0..b12fb390947 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/ICollection.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/ICollection.php @@ -1,5 +1,7 @@ caldavBackend = $caldavBackend; $this->principalUri = $principalUri; $this->notification = $notification; - } /** - * Returns the path name for this notification + * Returns the path name for this notification. * * @return string */ - function getName() { - - return $this->notification->getId() . '.xml'; - + public function getName() + { + return $this->notification->getId().'.xml'; } /** @@ -76,10 +76,9 @@ class Node extends DAV\File implements INode, DAVACL\IACL { * * @return string */ - function getETag() { - + public function getETag() + { return $this->notification->getETag(); - } /** @@ -88,34 +87,28 @@ class Node extends DAV\File implements INode, DAVACL\IACL { * * @return NotificationInterface */ - function getNotificationType() { - + public function getNotificationType() + { return $this->notification; - } /** - * Deletes this notification - * - * @return void + * Deletes this notification. */ - function delete() { - + public function delete() + { $this->caldavBackend->deleteNotification($this->getOwner(), $this->notification); - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->principalUri; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/Plugin.php index e742351f5be..182b70c8ec3 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/Plugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Notifications/Plugin.php @@ -1,5 +1,7 @@ server = $server; $server->on('method:GET', [$this, 'httpGet'], 90); - $server->on('propFind', [$this, 'propFind']); + $server->on('propFind', [$this, 'propFind']); $server->xml->namespaceMap[self::NS_CALENDARSERVER] = 'cs'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification'; + $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{'.self::NS_CALENDARSERVER.'}notification'; array_push($server->protectedProperties, - '{' . self::NS_CALENDARSERVER . '}notification-URL', - '{' . self::NS_CALENDARSERVER . '}notificationtype' + '{'.self::NS_CALENDARSERVER.'}notification-URL', + '{'.self::NS_CALENDARSERVER.'}notificationtype' ); - } /** - * PropFind + * PropFind. * - * @param PropFind $propFind + * @param PropFind $propFind * @param BaseINode $node - * @return void */ - function propFind(PropFind $propFind, BaseINode $node) { - + public 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) { + $propFind->handle('{'.self::NS_CALENDARSERVER.'}notification-URL', function () use ($principalUrl, $caldavPlugin) { + $notificationPath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl).'/notifications/'; - $notificationPath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl) . '/notifications/'; return new DAV\Xml\Property\Href($notificationPath); - }); - } if ($node instanceof INode) { - $propFind->handle( - '{' . self::NS_CALENDARSERVER . '}notificationtype', + '{'.self::NS_CALENDARSERVER.'}notificationtype', [$node, 'getNotificationType'] ); - } - } /** @@ -121,12 +113,11 @@ class Plugin extends ServerPlugin { * We use this to intercept GET calls to notification nodes, and return the * proper response. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function httpGet(RequestInterface $request, ResponseInterface $response) { - + public function httpGet(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); try { @@ -135,8 +126,9 @@ class Plugin extends ServerPlugin { return; } - if (!$node instanceof INode) + if (!$node instanceof INode) { return; + } $writer = $this->server->xml->getWriter(); $writer->contextUri = $this->server->getBaseUri(); @@ -153,7 +145,6 @@ class Plugin extends ServerPlugin { // Return false to break the event chain. return false; - } /** @@ -167,14 +158,12 @@ class Plugin extends ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds support for caldav-notifications, which is required to enable caldav-sharing.', - 'link' => 'http://sabre.io/dav/caldav-sharing/', + 'link' => 'http://sabre.io/dav/caldav-sharing/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Plugin.php index def11d52dff..04eaedf73aa 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Plugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Plugin.php @@ -1,8 +1,11 @@ server->tree->getNodeForPath($parent); if ($node instanceof DAV\IExtendedCollection) { @@ -82,8 +90,8 @@ class Plugin extends DAV\ServerPlugin { return ['MKCALENDAR']; } } - return []; + return []; } /** @@ -94,10 +102,11 @@ class Plugin extends DAV\ServerPlugin { * a calendar home. * * @param string $principalUrl + * * @return string */ - function getCalendarHomeForPrincipal($principalUrl) { - + public function getCalendarHomeForPrincipal($principalUrl) + { // The default behavior for most sabre/dav servers is that there is a // principals root node, which contains users directly under it. // @@ -106,11 +115,14 @@ class Plugin extends DAV\ServerPlugin { // excludes things like the calendar-proxy-read principal (which it // should). $parts = explode('/', trim($principalUrl, '/')); - if (count($parts) !== 2) return; - if ($parts[0] !== 'principals') return; - - return self::CALENDAR_ROOT . '/' . $parts[1]; + if (2 !== count($parts)) { + return; + } + if ('principals' !== $parts[0]) { + return; + } + return self::CALENDAR_ROOT.'/'.$parts[1]; } /** @@ -118,10 +130,9 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getFeatures() { - + public function getFeatures() + { return ['calendar-access', 'calendar-proxy']; - } /** @@ -132,10 +143,9 @@ class Plugin extends DAV\ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'caldav'; - } /** @@ -146,19 +156,20 @@ class Plugin extends DAV\ServerPlugin { * implement them * * @param string $uri + * * @return array */ - function getSupportedReportSet($uri) { - + public 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'; + $reports[] = '{'.self::NS_CALDAV.'}calendar-multiget'; + $reports[] = '{'.self::NS_CALDAV.'}calendar-query'; } if ($node instanceof ICalendar) { - $reports[] = '{' . self::NS_CALDAV . '}free-busy-query'; + $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 @@ -166,39 +177,38 @@ class Plugin extends DAV\ServerPlugin { if ($node instanceof CalendarHome && $this->server->getPlugin('sync')) { $reports[] = '{DAV:}sync-collection'; } - return $reports; + return $reports; } /** - * Initializes the plugin + * Initializes the plugin. * * @param DAV\Server $server - * @return void */ - function initialize(DAV\Server $server) { - + public 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('beforeCreateFile', [$this, 'beforeCreateFile']); - $server->on('beforeWriteContent', [$this, 'beforeWriteContent']); - $server->on('afterMethod:GET', [$this, 'httpAfterGET']); + $server->on('method:MKCALENDAR', [$this, 'httpMkCalendar']); + $server->on('report', [$this, 'report']); + $server->on('propFind', [$this, 'propFind']); + $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); + $server->on('beforeCreateFile', [$this, 'beforeCreateFile']); + $server->on('beforeWriteContent', [$this, 'beforeWriteContent']); + $server->on('afterMethod:GET', [$this, 'httpAfterGET']); $server->on('getSupportedPrivilegeSet', [$this, 'getSupportedPrivilegeSet']); $server->xml->namespaceMap[self::NS_CALDAV] = 'cal'; $server->xml->namespaceMap[self::NS_CALENDARSERVER] = 'cs'; - $server->xml->elementMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'; - $server->xml->elementMap['{' . self::NS_CALDAV . '}calendar-query'] = 'Sabre\\CalDAV\\Xml\\Request\\CalendarQueryReport'; - $server->xml->elementMap['{' . self::NS_CALDAV . '}calendar-multiget'] = 'Sabre\\CalDAV\\Xml\\Request\\CalendarMultiGetReport'; - $server->xml->elementMap['{' . self::NS_CALDAV . '}free-busy-query'] = 'Sabre\\CalDAV\\Xml\\Request\\FreeBusyQueryReport'; - $server->xml->elementMap['{' . self::NS_CALDAV . '}mkcalendar'] = 'Sabre\\CalDAV\\Xml\\Request\\MkCalendar'; - $server->xml->elementMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp'; - $server->xml->elementMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'; + $server->xml->elementMap['{'.self::NS_CALDAV.'}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'; + $server->xml->elementMap['{'.self::NS_CALDAV.'}calendar-query'] = 'Sabre\\CalDAV\\Xml\\Request\\CalendarQueryReport'; + $server->xml->elementMap['{'.self::NS_CALDAV.'}calendar-multiget'] = 'Sabre\\CalDAV\\Xml\\Request\\CalendarMultiGetReport'; + $server->xml->elementMap['{'.self::NS_CALDAV.'}free-busy-query'] = 'Sabre\\CalDAV\\Xml\\Request\\FreeBusyQueryReport'; + $server->xml->elementMap['{'.self::NS_CALDAV.'}mkcalendar'] = 'Sabre\\CalDAV\\Xml\\Request\\MkCalendar'; + $server->xml->elementMap['{'.self::NS_CALDAV.'}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp'; + $server->xml->elementMap['{'.self::NS_CALDAV.'}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'; $server->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; @@ -206,86 +216,84 @@ class Plugin extends DAV\ServerPlugin { $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', + '{'.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' - + '{'.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'; + $aclPlugin->principalSearchPropertySet['{'.self::NS_CALDAV.'}calendar-user-address-set'] = 'Calendar address'; } } /** - * This functions handles REPORT requests specific to CalDAV + * This functions handles REPORT requests specific to CalDAV. * * @param string $reportName - * @param mixed $report - * @param mixed $path + * @param mixed $report + * @param mixed $path + * * @return bool */ - function report($reportName, $report, $path) { - + public function report($reportName, $report, $path) + { switch ($reportName) { - case '{' . self::NS_CALDAV . '}calendar-multiget' : + case '{'.self::NS_CALDAV.'}calendar-multiget': $this->server->transactionType = 'report-calendar-multiget'; $this->calendarMultiGetReport($report); + return false; - case '{' . self::NS_CALDAV . '}calendar-query' : + case '{'.self::NS_CALDAV.'}calendar-query': $this->server->transactionType = 'report-calendar-query'; $this->calendarQueryReport($report); + return false; - case '{' . self::NS_CALDAV . '}free-busy-query' : + case '{'.self::NS_CALDAV.'}free-busy-query': $this->server->transactionType = 'report-free-busy-query'; $this->freeBusyQueryReport($report); + return false; - } - - } /** * This function handles the MKCALENDAR HTTP method, which creates * a new calendar. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpMkCalendar(RequestInterface $request, ResponseInterface $response) { - + public function httpMkCalendar(RequestInterface $request, ResponseInterface $response) + { $body = $request->getBodyAsString(); $path = $request->getPath(); $properties = []; if ($body) { - try { $mkcalendar = $this->server->xml->expect( '{urn:ietf:params:xml:ns:caldav}mkcalendar', $body ); } catch (\Sabre\Xml\ParseException $e) { - throw new BadRequest($e->getMessage(), null, $e); + throw new BadRequest($e->getMessage(), 0, $e); } $properties = $mkcalendar->getProperties(); - } // iCal abuses MKCALENDAR since iCal 10.9.2 to create server-stored @@ -297,7 +305,7 @@ class Plugin extends DAV\ServerPlugin { if (isset($properties['{DAV:}resourcetype'])) { $resourceType = $properties['{DAV:}resourcetype']->getValue(); } else { - $resourceType = ['{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar']; + $resourceType = ['{DAV:}collection', '{urn:ietf:params:xml:ns:caldav}calendar']; } $this->server->createCollection($path, new MkCol($resourceType, $properties)); @@ -310,80 +318,77 @@ class Plugin extends DAV\ServerPlugin { } /** - * PropFind + * 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 + * @param DAV\INode $node */ - function propFind(DAV\PropFind $propFind, DAV\INode $node) { - - $ns = '{' . self::NS_CALDAV . '}'; + public 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() { + $propFind->handle($ns.'max-resource-size', $this->maxResourceSize); + $propFind->handle($ns.'supported-calendar-data', function () { return new Xml\Property\SupportedCalendarData(); }); - $propFind->handle($ns . 'supported-collation-set', function() { + $propFind->handle($ns.'supported-collation-set', function () { return new Xml\Property\SupportedCollationSet(); }); - } if ($node instanceof DAVACL\IPrincipal) { - $principalUrl = $node->getPrincipalUrl(); - $propFind->handle('{' . self::NS_CALDAV . '}calendar-home-set', function() use ($principalUrl) { - + $propFind->handle('{'.self::NS_CALDAV.'}calendar-home-set', function () use ($principalUrl) { $calendarHomePath = $this->getCalendarHomeForPrincipal($principalUrl); - if (is_null($calendarHomePath)) return null; - return new LocalHref($calendarHomePath . '/'); + if (is_null($calendarHomePath)) { + return null; + } + return new LocalHref($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) { + $propFind->handle('{'.self::NS_CALDAV.'}calendar-user-address-set', function () use ($node) { $addresses = $node->getAlternateUriSet(); - $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . '/'; + $addresses[] = $this->server->getBaseUri().$node->getPrincipalUrl().'/'; + return new LocalHref($addresses); }); // 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) { + $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:') { + if ('mailto:' === substr($address, 0, 7)) { $emails[] = substr($address, 7); } } + return new Xml\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) { + $propRead = '{'.self::NS_CALENDARSERVER.'}calendar-proxy-read-for'; + $propWrite = '{'.self::NS_CALENDARSERVER.'}calendar-proxy-write-for'; + if (404 === $propFind->getStatus($propRead) || 404 === $propFind->getStatus($propWrite)) { $aclPlugin = $this->server->getPlugin('acl'); $membership = $aclPlugin->getPrincipalMembership($propFind->getPath()); $readList = []; $writeList = []; foreach ($membership as $group) { - $groupNode = $this->server->tree->getNodeForPath($group); - $listItem = Uri\split($group)[0] . '/'; + $listItem = Uri\split($group)[0].'/'; // If the node is either ap proxy-read or proxy-write // group, we grab the parent principal and add it to the @@ -394,33 +399,27 @@ class Plugin extends DAV\ServerPlugin { if ($groupNode instanceof Principal\IProxyWrite) { $writeList[] = $listItem; } - } $propFind->set($propRead, new LocalHref($readList)); $propFind->set($propWrite, new LocalHref($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('{' . self::NS_CALDAV . '}calendar-data', function() use ($node) { + $propFind->handle('{'.self::NS_CALDAV.'}calendar-data', function () use ($node) { $val = $node->get(); - if (is_resource($val)) + if (is_resource($val)) { $val = stream_get_contents($val); + } // Taking out \r to not screw up the xml output - return str_replace("\r", "", $val); - + return str_replace("\r", '', $val); }); - } - } /** @@ -430,11 +429,10 @@ class Plugin extends DAV\ServerPlugin { * of urls. Effectively avoiding a lot of redundant requests. * * @param CalendarMultiGetReport $report - * @return void */ - function calendarMultiGetReport($report) { - - $needsJson = $report->contentType === 'application/calendar+json'; + public function calendarMultiGetReport($report) + { + $needsJson = 'application/calendar+json' === $report->contentType; $timeZones = []; $propertyList = []; @@ -445,9 +443,8 @@ class Plugin extends DAV\ServerPlugin { ); foreach ($this->server->getPropertiesForMultiplePaths($paths, $report->properties) as $uri => $objProps) { - - if (($needsJson || $report->expand) && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) { - $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']); + if (($needsJson || $report->expand) && isset($objProps[200]['{'.self::NS_CALDAV.'}calendar-data'])) { + $vObject = VObject\Reader::read($objProps[200]['{'.self::NS_CALDAV.'}calendar-data']); if ($report->expand) { // We're expanding, and for that we need to figure out the @@ -455,7 +452,7 @@ class Plugin extends DAV\ServerPlugin { list($calendarPath) = Uri\split($uri); if (!isset($timeZones[$calendarPath])) { // Checking the calendar-timezone property. - $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; + $tzProp = '{'.self::NS_CALDAV.'}calendar-timezone'; $tzResult = $this->server->getProperties($calendarPath, [$tzProp]); if (isset($tzResult[$tzProp])) { // This property contains a VCALENDAR with a single @@ -472,9 +469,9 @@ class Plugin extends DAV\ServerPlugin { $vObject = $vObject->expand($report->expand['start'], $report->expand['end'], $timeZones[$calendarPath]); } if ($needsJson) { - $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); + $objProps[200]['{'.self::NS_CALDAV.'}calendar-data'] = json_encode($vObject->jsonSerialize()); } else { - $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + $objProps[200]['{'.self::NS_CALDAV.'}calendar-data'] = $vObject->serialize(); } // Destroy circular references so PHP will garbage collect the // object. @@ -482,7 +479,6 @@ class Plugin extends DAV\ServerPlugin { } $propertyList[] = $objProps; - } $prefer = $this->server->getHTTPPrefer(); @@ -490,24 +486,22 @@ class Plugin extends DAV\ServerPlugin { $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->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, 'minimal' === $prefer['return'])); } /** - * This function handles the calendar-query REPORT + * This function handles the calendar-query REPORT. * * This report is used by clients to request calendar objects based on * complex conditions. * * @param Xml\Request\CalendarQueryReport $report - * @return void */ - function calendarQueryReport($report) { - + public function calendarQueryReport($report) + { $path = $this->server->getRequestUri(); - $needsJson = $report->contentType === 'application/calendar+json'; + $needsJson = 'application/calendar+json' === $report->contentType; $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); $depth = $this->server->getHTTPDepth(0); @@ -519,7 +513,7 @@ class Plugin extends DAV\ServerPlugin { if ($report->expand) { // We're expanding, and for that we need to figure out the // calendar's timezone. - $tzProp = '{' . self::NS_CALDAV . '}calendar-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 @@ -538,13 +532,11 @@ class Plugin extends DAV\ServerPlugin { // The calendarobject was requested directly. In this case we handle // this locally. - if ($depth == 0 && $node instanceof ICalendarObject) { - + if (0 == $depth && $node instanceof ICalendarObject) { $requestedCalendarData = true; $requestedProperties = $report->properties; 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'; @@ -566,43 +558,35 @@ class Plugin extends DAV\ServerPlugin { // 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, $report->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 ($report->expand) { $vObject = $vObject->expand($report->expand['start'], $report->expand['end'], $calendarTimeZone); } if ($needsJson) { - $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); + $properties[200]['{'.self::NS_CALDAV.'}calendar-data'] = json_encode($vObject->jsonSerialize()); } elseif ($report->expand) { - $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + $properties[200]['{'.self::NS_CALDAV.'}calendar-data'] = $vObject->serialize(); } } $result = [$properties]; - } // Destroy circular references so PHP will garbage collect the // object. $vObject->destroy(); - } - } - if ($node instanceof ICalendarObjectContainer && $depth === 0) { - - if (strpos($this->server->httpRequest->getHeader('User-Agent'), 'MSFT-') === 0) { + if ($node instanceof ICalendarObjectContainer && 0 === $depth) { + if (0 === strpos((string) $this->server->httpRequest->getHeader('User-Agent'), 'MSFT-')) { // Microsoft clients incorrectly supplied depth as 0, when it actually // should have set depth to 1. We're implementing a workaround here // to deal with this. @@ -614,31 +598,28 @@ class Plugin extends DAV\ServerPlugin { } 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) { - + if ($node instanceof ICalendarObjectContainer && 1 == $depth) { $nodePaths = $node->calendarQuery($report->filters); foreach ($nodePaths as $path) { - list($properties) = - $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $report->properties); + $this->server->getPropertiesForPath($this->server->getRequestUri().'/'.$path, $report->properties); if (($needsJson || $report->expand)) { - $vObject = VObject\Reader::read($properties[200]['{' . self::NS_CALDAV . '}calendar-data']); + $vObject = VObject\Reader::read($properties[200]['{'.self::NS_CALDAV.'}calendar-data']); if ($report->expand) { $vObject = $vObject->expand($report->expand['start'], $report->expand['end'], $calendarTimeZone); } if ($needsJson) { - $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize()); + $properties[200]['{'.self::NS_CALDAV.'}calendar-data'] = json_encode($vObject->jsonSerialize()); } else { - $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); + $properties[200]['{'.self::NS_CALDAV.'}calendar-data'] = $vObject->serialize(); } // Destroy circular references so PHP will garbage collect the @@ -646,9 +627,7 @@ class Plugin extends DAV\ServerPlugin { $vObject->destroy(); } $result[] = $properties; - } - } $prefer = $this->server->getHTTPPrefer(); @@ -656,8 +635,7 @@ class Plugin extends DAV\ServerPlugin { $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->server->httpResponse->setBody($this->server->generateMultiStatus($result, 'minimal' === $prefer['return'])); } /** @@ -665,15 +643,14 @@ class Plugin extends DAV\ServerPlugin { * response for the CALDAV:free-busy-query REPORT. * * @param Xml\Request\FreeBusyQueryReport $report - * @return void */ - protected function freeBusyQueryReport(Xml\Request\FreeBusyQueryReport $report) { - + protected function freeBusyQueryReport(Xml\Request\FreeBusyQueryReport $report) + { $uri = $this->server->getRequestUri(); $acl = $this->server->getPlugin('acl'); if ($acl) { - $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy'); + $acl->checkPrivileges($uri, '{'.self::NS_CALDAV.'}read-free-busy'); } $calendar = $this->server->tree->getNodeForPath($uri); @@ -681,7 +658,7 @@ class Plugin extends DAV\ServerPlugin { throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars'); } - $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; + $tzProp = '{'.self::NS_CALDAV.'}calendar-timezone'; // Figuring out the default timezone for the calendar, for floating // times. @@ -699,26 +676,27 @@ class Plugin extends DAV\ServerPlugin { // Doing a calendar-query first, to make sure we get the most // performance. $urls = $calendar->calendarQuery([ - 'name' => 'VCALENDAR', + 'name' => 'VCALENDAR', 'comp-filters' => [ [ - 'name' => 'VEVENT', - 'comp-filters' => [], - 'prop-filters' => [], + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], 'is-not-defined' => false, - 'time-range' => [ + 'time-range' => [ 'start' => $report->start, - 'end' => $report->end, + 'end' => $report->end, ], ], ], - 'prop-filters' => [], + 'prop-filters' => [], 'is-not-defined' => false, - 'time-range' => null, + 'time-range' => null, ]); - $objects = array_map(function($url) use ($calendar) { + $objects = array_map(function ($url) use ($calendar) { $obj = $calendar->getChild($url)->get(); + return $obj; }, $urls); @@ -733,7 +711,6 @@ class Plugin extends DAV\ServerPlugin { $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->setHeader('Content-Length', strlen($result)); $this->server->httpResponse->setBody($result); - } /** @@ -742,17 +719,17 @@ class Plugin extends DAV\ServerPlugin { * This plugin uses this method to ensure that CalDAV objects receive * valid calendar data. * - * @param string $path + * @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 + * @param resource $data + * @param bool $modified should be set to true, if this event handler + * changed &$data */ - function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) { - - if (!$node instanceof ICalendarObject) + public 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 @@ -760,8 +737,9 @@ class Plugin extends DAV\ServerPlugin { list($parent) = Uri\split($path); $parentNode = $this->server->tree->getNodeForPath($parent); - if (!$parentNode instanceof ICalendar) + if (!$parentNode instanceof ICalendar) { return; + } $this->validateICalendar( $data, @@ -771,7 +749,6 @@ class Plugin extends DAV\ServerPlugin { $this->server->httpResponse, false ); - } /** @@ -780,17 +757,17 @@ class Plugin extends DAV\ServerPlugin { * This plugin uses this method to ensure that newly created calendar * objects contain valid calendar data. * - * @param string $path - * @param resource $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 + * @param bool $modified should be set to true, if this event handler + * changed &$data */ - function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) { - - if (!$parentNode instanceof ICalendar) + public function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) + { + if (!$parentNode instanceof ICalendar) { return; + } $this->validateICalendar( $data, @@ -800,7 +777,6 @@ class Plugin extends DAV\ServerPlugin { $this->server->httpResponse, true ); - } /** @@ -808,17 +784,16 @@ class Plugin extends DAV\ServerPlugin { * * 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 + * @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 */ - protected function validateICalendar(&$data, $path, &$modified, RequestInterface $request, ResponseInterface $response, $isNew) { - + 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); @@ -827,10 +802,9 @@ class Plugin extends DAV\ServerPlugin { $before = $data; try { - // If the data starts with a [, we can reasonably assume we're dealing // with a jCal object. - if (substr($data, 0, 1) === '[') { + if ('[' === substr($data, 0, 1)) { $vobj = VObject\Reader::readJson($data); // Converting $data back to iCalendar, as that's what we @@ -840,14 +814,11 @@ class Plugin extends DAV\ServerPlugin { } 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()); - + throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: '.$e->getMessage()); } - if ($vobj->name !== 'VCALENDAR') { + if ('VCALENDAR' !== $vobj->name) { throw new DAV\Exception\UnsupportedMediaType('This collection can only support iCalendar objects.'); } @@ -867,25 +838,24 @@ class Plugin extends DAV\ServerPlugin { foreach ($vobj->getComponents() as $component) { switch ($component->name) { - case 'VTIMEZONE' : + case 'VTIMEZONE': continue 2; - case 'VEVENT' : - case 'VTODO' : - case 'VJOURNAL' : + case 'VEVENT': + case 'VTODO': + case 'VJOURNAL': $foundType = $component->name; break; } - } if (!$foundType || !in_array($foundType, $supportedComponents)) { - throw new Exception\InvalidComponentType('iCalendar objects must at least have a component of type ' . implode(', ', $supportedComponents)); + throw new Exception\InvalidComponentType('iCalendar objects must at least have a component of type '.implode(', ', $supportedComponents)); } $options = VObject\Node::PROFILE_CALDAV; $prefer = $this->server->getHTTPPrefer(); - if ($prefer['handling'] !== 'strict') { + if ('strict' !== $prefer['handling']) { $options |= VObject\Node::REPAIR; } @@ -897,32 +867,28 @@ class Plugin extends DAV\ServerPlugin { // $messages contains a list of problems with the vcard, along with // their severity. foreach ($messages as $message) { - if ($message['level'] > $highestLevel) { // Recording the highest reported error level. $highestLevel = $message['level']; $warningMessage = $message['message']; } switch ($message['level']) { - - case 1 : + case 1: // Level 1 means that there was a problem, but it was repaired. $modified = true; break; - case 2 : + case 2: // Level 2 means a warning, but not critical break; - case 3 : + case 3: // Level 3 means a critical error - throw new DAV\Exception\UnsupportedMediaType('Validation error in iCalendar: ' . $message['message']); - + throw new DAV\Exception\UnsupportedMediaType('Validation error in iCalendar: '.$message['message']); } - } if ($warningMessage) { $response->setHeader( 'X-Sabre-Ew-Gross', - 'iCalendar validation warning: ' . $warningMessage + 'iCalendar validation warning: '.$warningMessage ); } @@ -940,7 +906,7 @@ class Plugin extends DAV\ServerPlugin { $vobj, $parentPath, &$subModified, - $isNew + $isNew, ] ); @@ -949,15 +915,13 @@ class Plugin extends DAV\ServerPlugin { $data = $vobj->serialize(); // Using md5 to figure out if there was an *actual* change. - if (!$modified && strcmp($data, $before) !== 0) { + if (!$modified && 0 !== strcmp($data, $before)) { $modified = true; } - } // Destroy circular references so PHP will garbage collect the object. $vobj->destroy(); - } /** @@ -967,11 +931,11 @@ class Plugin extends DAV\ServerPlugin { * @param INode $node * @param array $supportedPrivilegeSet */ - function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) { - + public function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) + { if ($node instanceof ICalendar) { - $supportedPrivilegeSet['{DAV:}read']['aggregates']['{' . self::NS_CALDAV . '}read-free-busy'] = [ - 'abstract' => false, + $supportedPrivilegeSet['{DAV:}read']['aggregates']['{'.self::NS_CALDAV.'}read-free-busy'] = [ + 'abstract' => false, 'aggregates' => [], ]; } @@ -983,18 +947,20 @@ class Plugin extends DAV\ServerPlugin { * can use to create new calendars. * * @param DAV\INode $node - * @param string $output + * @param string $output + * * @return bool */ - function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof CalendarHome) + public function htmlActionsPanel(DAV\INode $node, &$output) + { + if (!$node instanceof CalendarHome) { return; + } $output .= '

Create new calendar

- +

@@ -1002,7 +968,6 @@ class Plugin extends DAV\ServerPlugin { '; return false; - } /** @@ -1010,22 +975,22 @@ class Plugin extends DAV\ServerPlugin { * * This is used to transform data into jCal, if this was requested. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function httpAfterGet(RequestInterface $request, ResponseInterface $response) { - - if (strpos($response->getHeader('Content-Type'), 'text/calendar') === false) { + public function httpAfterGet(RequestInterface $request, ResponseInterface $response) + { + $contentType = $response->getHeader('Content-Type'); + if (null === $contentType || false === strpos($contentType, 'text/calendar')) { return; } - $result = HTTP\Util::negotiate( + $result = HTTP\negotiateContentType( $request->getHeader('Accept'), ['text/calendar', 'application/calendar+json'] ); - if ($result !== 'application/calendar+json') { + if ('application/calendar+json' !== $result) { // Do nothing return; } @@ -1041,7 +1006,6 @@ class Plugin extends DAV\ServerPlugin { $response->setHeader('Content-Type', 'application/calendar+json'); $response->setHeader('Content-Length', strlen($jsonBody)); - } /** @@ -1055,14 +1019,12 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds support for CalDAV (rfc4791)', - 'link' => 'http://sabre.io/dav/caldav/', + 'link' => 'http://sabre.io/dav/caldav/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/Collection.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/Collection.php index e19719a76d8..cae29c752a9 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/Collection.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/Collection.php @@ -1,11 +1,13 @@ principalBackend, $principalInfo); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/IProxyRead.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/IProxyRead.php index 7dd3759329c..96e6991c5ad 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/IProxyRead.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/IProxyRead.php @@ -1,11 +1,13 @@ principalInfo = $principalInfo; $this->principalBackend = $principalBackend; - } /** @@ -52,103 +53,91 @@ class ProxyRead implements IProxyRead { * * @return string */ - function getName() { - + public function getName() + { return 'calendar-proxy-read'; - } /** - * Returns the last modification time - * - * @return null + * Returns the last modification time. */ - function getLastModified() { - + public function getLastModified() + { return null; - } /** - * Deletes the current node + * Deletes the current node. * * @throws DAV\Exception\Forbidden - * @return void */ - function delete() { - + public function delete() + { throw new DAV\Exception\Forbidden('Permission denied to delete node'); - } /** - * Renames the node + * Renames the node. * * @param string $name The new name + * * @throws DAV\Exception\Forbidden - * @return void */ - function setName($name) { - + public function setName($name) + { throw new DAV\Exception\Forbidden('Permission denied to rename file'); - } - /** - * Returns a list of alternative urls for a principal + * Returns a list of alternative urls for a principal. * * This can for example be an email address, or ldap url. * * @return array */ - function getAlternateUriSet() { - + public function getAlternateUriSet() + { return []; - } /** - * Returns the full principal url + * Returns the full principal url. * * @return string */ - function getPrincipalUrl() { - - return $this->principalInfo['uri'] . '/' . $this->getName(); - + public function getPrincipalUrl() + { + return $this->principalInfo['uri'].'/'.$this->getName(); } /** - * Returns the list of group members + * 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() { - + public function getGroupMemberSet() + { return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); - } /** - * Returns the list of groups this principal is member of + * 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() { - + public function getGroupMembership() + { return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); - } /** - * Sets a list of group members + * 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. @@ -156,26 +145,22 @@ class ProxyRead implements IProxyRead { * This method should throw an exception if the members could not be set. * * @param array $principals - * @return void */ - function setGroupMemberSet(array $principals) { - + public function setGroupMemberSet(array $principals) + { $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); - } /** - * Returns the displayname + * Returns the displayname. * * This should be a human readable name for the principal. * If none is available, return the nodename. * * @return string */ - function getDisplayName() { - + public function getDisplayName() + { return $this->getName(); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php index 43dd9bf07bf..7b9c78e0ab2 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php @@ -1,12 +1,14 @@ principalInfo = $principalInfo; $this->principalBackend = $principalBackend; - } /** @@ -52,103 +53,91 @@ class ProxyWrite implements IProxyWrite { * * @return string */ - function getName() { - + public function getName() + { return 'calendar-proxy-write'; - } /** - * Returns the last modification time - * - * @return null + * Returns the last modification time. */ - function getLastModified() { - + public function getLastModified() + { return null; - } /** - * Deletes the current node + * Deletes the current node. * * @throws DAV\Exception\Forbidden - * @return void */ - function delete() { - + public function delete() + { throw new DAV\Exception\Forbidden('Permission denied to delete node'); - } /** - * Renames the node + * Renames the node. * * @param string $name The new name + * * @throws DAV\Exception\Forbidden - * @return void */ - function setName($name) { - + public function setName($name) + { throw new DAV\Exception\Forbidden('Permission denied to rename file'); - } - /** - * Returns a list of alternative urls for a principal + * Returns a list of alternative urls for a principal. * * This can for example be an email address, or ldap url. * * @return array */ - function getAlternateUriSet() { - + public function getAlternateUriSet() + { return []; - } /** - * Returns the full principal url + * Returns the full principal url. * * @return string */ - function getPrincipalUrl() { - - return $this->principalInfo['uri'] . '/' . $this->getName(); - + public function getPrincipalUrl() + { + return $this->principalInfo['uri'].'/'.$this->getName(); } /** - * Returns the list of group members + * 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() { - + public function getGroupMemberSet() + { return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); - } /** - * Returns the list of groups this principal is member of + * 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() { - + public function getGroupMembership() + { return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); - } /** - * Sets a list of group members + * 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. @@ -156,26 +145,22 @@ class ProxyWrite implements IProxyWrite { * This method should throw an exception if the members could not be set. * * @param array $principals - * @return void */ - function setGroupMemberSet(array $principals) { - + public function setGroupMemberSet(array $principals) + { $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals); - } /** - * Returns the displayname + * Returns the displayname. * * This should be a human readable name for the principal. * If none is available, return the nodename. * * @return string */ - function getDisplayName() { - + public function getDisplayName() + { return $this->getName(); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/User.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/User.php index 6e97e7cca56..88bf4b4f775 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/User.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/User.php @@ -1,12 +1,14 @@ principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/' . $name); + public function getChild($name) + { + $principal = $this->principalBackend->getPrincipalByPath($this->getPrincipalURL().'/'.$name); if (!$principal) { - throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); + throw new DAV\Exception\NotFound('Node with name '.$name.' was not found'); } - if ($name === 'calendar-proxy-read') + if ('calendar-proxy-read' === $name) { return new ProxyRead($this->principalBackend, $this->principalProperties); + } - if ($name === 'calendar-proxy-write') + if ('calendar-proxy-write' === $name) { return new ProxyWrite($this->principalBackend, $this->principalProperties); + } - throw new DAV\Exception\NotFound('Node with name ' . $name . ' was not found'); - + throw new DAV\Exception\NotFound('Node with name '.$name.' was not found'); } /** - * Returns an array with all the child nodes + * Returns an array with all the child nodes. * * @return DAV\INode[] */ - function getChildren() { - + public function getChildren() + { $r = []; - if ($this->principalBackend->getPrincipalByPath($this->getPrincipalURL() . '/calendar-proxy-read')) { + 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')) { + 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 + * Returns whether or not the child node exists. * * @param string $name + * * @return bool */ - function childExists($name) { - + public function childExists($name) + { try { $this->getChild($name); + return true; } catch (DAV\Exception\NotFound $e) { return false; } - } /** @@ -115,21 +117,20 @@ class User extends DAVACL\Principal implements DAV\ICollection { * * @return array */ - function getACL() { - + public function getACL() + { $acl = parent::getACL(); $acl[] = [ 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read', + 'principal' => $this->principalProperties['uri'].'/calendar-proxy-read', 'protected' => true, ]; $acl[] = [ 'privilege' => '{DAV:}read', - 'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write', + 'principal' => $this->principalProperties['uri'].'/calendar-proxy-write', 'protected' => true, ]; + return $acl; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IInbox.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IInbox.php index c9fd77d9353..64a94becc8a 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IInbox.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IInbox.php @@ -1,5 +1,7 @@ senderEmail = $senderEmail; - } /* @@ -60,10 +61,9 @@ class IMipPlugin extends DAV\ServerPlugin { * @param DAV\Server $server * @return void */ - function initialize(DAV\Server $server) { - + public function initialize(DAV\Server $server) + { $server->on('schedule', [$this, 'schedule'], 120); - } /** @@ -74,67 +74,68 @@ class IMipPlugin extends DAV\ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'imip'; - } /** * Event handler for the 'schedule' event. * * @param ITip\Message $iTipMessage - * @return void */ - function schedule(ITip\Message $iTipMessage) { - + public 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') + if ('mailto' !== parse_url($iTipMessage->sender, PHP_URL_SCHEME)) { return; + } - if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') + if ('mailto' !== parse_url($iTipMessage->recipient, PHP_URL_SCHEME)) { return; + } $sender = substr($iTipMessage->sender, 7); $recipient = substr($iTipMessage->recipient, 7); if ($iTipMessage->senderName) { - $sender = $iTipMessage->senderName . ' <' . $sender . '>'; + $sender = $iTipMessage->senderName.' <'.$sender.'>'; } - if ($iTipMessage->recipientName) { - $recipient = $iTipMessage->recipientName . ' <' . $recipient . '>'; + if ($iTipMessage->recipientName && $iTipMessage->recipientName != $recipient) { + $recipient = $iTipMessage->recipientName.' <'.$recipient.'>'; } $subject = 'SabreDAV iTIP message'; switch (strtoupper($iTipMessage->method)) { - case 'REPLY' : - $subject = 'Re: ' . $summary; + case 'REPLY': + $subject = 'Re: '.$summary; break; - case 'REQUEST' : - $subject = $summary; + case 'REQUEST': + $subject = 'Invitation: '.$summary; break; - case 'CANCEL' : - $subject = 'Cancelled: ' . $summary; + case 'CANCEL': + $subject = 'Cancelled: '.$summary; break; } $headers = [ - 'Reply-To: ' . $sender, - 'From: ' . $this->senderEmail, - 'Content-Type: text/calendar; charset=UTF-8; method=' . $iTipMessage->method, + 'Reply-To: '.$sender, + 'From: '.$iTipMessage->senderName.' <'.$this->senderEmail.'>', + 'Content-Type: text/calendar; charset=UTF-8; method='.$iTipMessage->method, ]; if (DAV\Server::$exposeVersion) { - $headers[] = 'X-Sabre-Version: ' . DAV\Version::VERSION; + $headers[] = 'X-Sabre-Version: '.DAV\Version::VERSION; } $this->mail( $recipient, @@ -143,7 +144,6 @@ class IMipPlugin extends DAV\ServerPlugin { $headers ); $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip'; - } // @codeCoverageIgnoreStart @@ -152,16 +152,14 @@ class IMipPlugin extends DAV\ServerPlugin { /** * This function is responsible for sending the actual email. * - * @param string $to Recipient email address + * @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 + * @param string $body iCalendar body + * @param array $headers List of headers */ - protected function mail($to, $subject, $body, array $headers) { - + protected function mail($to, $subject, $body, array $headers) + { mail($to, $subject, $body, implode("\r\n", $headers)); - } // @codeCoverageIgnoreEnd @@ -177,14 +175,12 @@ class IMipPlugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Email delivery (rfc6047) for CalDAV scheduling', - 'link' => 'http://sabre.io/dav/scheduling/', + 'link' => 'http://sabre.io/dav/scheduling/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IOutbox.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IOutbox.php index 88fbdc4114b..384b503dfee 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IOutbox.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/IOutbox.php @@ -1,5 +1,7 @@ caldavBackend = $caldavBackend; $this->principalUri = $principalUri; - } /** @@ -53,19 +54,18 @@ class Inbox extends DAV\Collection implements IInbox { * * @return string */ - function getName() { - + public function getName() + { return 'inbox'; - } /** - * Returns an array with all the child nodes + * Returns an array with all the child nodes. * * @return \Sabre\DAV\INode[] */ - function getChildren() { - + public function getChildren() + { $objs = $this->caldavBackend->getSchedulingObjects($this->principalUri); $children = []; foreach ($objs as $obj) { @@ -73,12 +73,12 @@ class Inbox extends DAV\Collection implements IInbox { $obj['principaluri'] = $this->principalUri; $children[] = new SchedulingObject($this->caldavBackend, $obj); } - return $children; + return $children; } /** - * Creates a new file in the directory + * 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. @@ -97,27 +97,26 @@ class Inbox extends DAV\Collection implements IInbox { * 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 string $name Name of the file * @param resource|string $data Initial payload - * @return null|string + * + * @return string|null */ - function createFile($name, $data = null) { - + public function createFile($name, $data = null) + { $this->caldavBackend->createSchedulingObject($this->principalUri, $name, $data); - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->principalUri; - } /** @@ -132,8 +131,8 @@ class Inbox extends DAV\Collection implements IInbox { * * @return array */ - function getACL() { - + public function getACL() + { return [ [ 'privilege' => '{DAV:}read', @@ -152,16 +151,15 @@ class Inbox extends DAV\Collection implements IInbox { ], [ 'privilege' => '{DAV:}unbind', - 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'principal' => $this->getOwner().'/calendar-proxy-write', 'protected' => true, ], [ - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-deliver', + 'privilege' => '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-deliver', 'principal' => '{DAV:}authenticated', 'protected' => true, ], ]; - } /** @@ -179,10 +177,11 @@ class Inbox extends DAV\Collection implements IInbox { * documented by \Sabre\CalDAV\CalendarQueryParser. * * @param array $filters + * * @return array */ - function calendarQuery(array $filters) { - + public function calendarQuery(array $filters) + { $result = []; $validator = new CalDAV\CalendarQueryValidator(); @@ -196,8 +195,7 @@ class Inbox extends DAV\Collection implements IInbox { // Destroy circular references to PHP will GC the object. $vObject->destroy(); } + return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Outbox.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Outbox.php index 888ea308626..1442c4cc655 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Outbox.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Outbox.php @@ -1,5 +1,7 @@ principalUri = $principalUri; - } /** @@ -46,34 +47,31 @@ class Outbox extends DAV\Collection implements IOutbox { * * @return string */ - function getName() { - + public function getName() + { return 'outbox'; - } /** - * Returns an array with all the child nodes + * Returns an array with all the child nodes. * * @return \Sabre\DAV\INode[] */ - function getChildren() { - + public function getChildren() + { return []; - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->principalUri; - } /** @@ -88,11 +86,11 @@ class Outbox extends DAV\Collection implements IOutbox { * * @return array */ - function getACL() { - + public function getACL() + { return [ [ - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-send', + 'privilege' => '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-send', 'principal' => $this->getOwner(), 'protected' => true, ], @@ -102,22 +100,20 @@ class Outbox extends DAV\Collection implements IOutbox { 'protected' => true, ], [ - 'privilege' => '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-send', - 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'privilege' => '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-send', + 'principal' => $this->getOwner().'/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'principal' => $this->getOwner().'/calendar-proxy-read', 'protected' => true, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'principal' => $this->getOwner().'/calendar-proxy-write', 'protected' => true, ], ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Plugin.php index 0b991e61979..d2c9b68e94e 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Plugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/Plugin.php @@ -1,5 +1,7 @@ server = $server; - $server->on('method:POST', [$this, 'httpPost']); - $server->on('propFind', [$this, 'propFind']); - $server->on('propPatch', [$this, 'propPatch']); - $server->on('calendarObjectChange', [$this, 'calendarObjectChange']); - $server->on('beforeUnbind', [$this, 'beforeUnbind']); - $server->on('schedule', [$this, 'scheduleLocalDelivery']); + $server->on('method:POST', [$this, 'httpPost']); + $server->on('propFind', [$this, 'propFind']); + $server->on('propPatch', [$this, 'propPatch']); + $server->on('calendarObjectChange', [$this, 'calendarObjectChange']); + $server->on('beforeUnbind', [$this, 'beforeUnbind']); + $server->on('schedule', [$this, 'scheduleLocalDelivery']); $server->on('getSupportedPrivilegeSet', [$this, 'getSupportedPrivilegeSet']); - $ns = '{' . self::NS_CALDAV . '}'; + $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'; + $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' + $ns.'schedule-inbox-URL', + $ns.'schedule-outbox-URL', + $ns.'calendar-user-address-set', + $ns.'calendar-user-type', + $ns.'schedule-default-calendar-URL' ); - } /** @@ -139,10 +137,11 @@ class Plugin extends ServerPlugin { * available for the specified uri. * * @param string $uri + * * @return array */ - function getHTTPMethods($uri) { - + public function getHTTPMethods($uri) + { try { $node = $this->server->tree->getNodeForPath($uri); } catch (NotFound $e) { @@ -154,21 +153,21 @@ class Plugin extends ServerPlugin { } return []; - } /** * This method handles POST request for the outbox. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpPost(RequestInterface $request, ResponseInterface $response) { - + public 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) { + if (!$contentType || 0 !== strpos($contentType, 'text/calendar')) { return; } @@ -180,8 +179,9 @@ class Plugin extends ServerPlugin { } catch (NotFound $e) { return; } - if (!$node instanceof IOutbox) + if (!$node instanceof IOutbox) { return; + } $this->server->transactionType = 'post-caldav-outbox'; $this->outboxRequest($node, $request, $response); @@ -189,7 +189,6 @@ class Plugin extends ServerPlugin { // Returning false breaks the event chain and tells the server we've // handled the request. return false; - } /** @@ -198,43 +197,36 @@ class Plugin extends ServerPlugin { * We use this event to add calendar-auto-schedule-specific properties. * * @param PropFind $propFind - * @param INode $node - * @return void + * @param INode $node */ - function propFind(PropFind $propFind, INode $node) { - + public function propFind(PropFind $propFind, INode $node) + { if ($node instanceof DAVACL\IPrincipal) { - $caldavPlugin = $this->server->getPlugin('caldav'); $principalUrl = $node->getPrincipalUrl(); // schedule-outbox-URL property - $propFind->handle('{' . self::NS_CALDAV . '}schedule-outbox-URL', function() use ($principalUrl, $caldavPlugin) { - + $propFind->handle('{'.self::NS_CALDAV.'}schedule-outbox-URL', function () use ($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); if (!$calendarHomePath) { return null; } - $outboxPath = $calendarHomePath . '/outbox/'; + $outboxPath = $calendarHomePath.'/outbox/'; return new LocalHref($outboxPath); - }); // schedule-inbox-URL property - $propFind->handle('{' . self::NS_CALDAV . '}schedule-inbox-URL', function() use ($principalUrl, $caldavPlugin) { - + $propFind->handle('{'.self::NS_CALDAV.'}schedule-inbox-URL', function () use ($principalUrl, $caldavPlugin) { $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl); if (!$calendarHomePath) { return null; } - $inboxPath = $calendarHomePath . '/inbox/'; + $inboxPath = $calendarHomePath.'/inbox/'; return new LocalHref($inboxPath); - }); - $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function() use ($principalUrl, $caldavPlugin) { - + $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); @@ -243,7 +235,7 @@ class Plugin extends ServerPlugin { return null; } - $sccs = '{' . self::NS_CALDAV . '}supported-calendar-component-set'; + $sccs = '{'.self::NS_CALDAV.'}supported-calendar-component-set'; $result = $this->server->getPropertiesForPath($calendarHomePath, [ '{DAV:}resourcetype', @@ -252,18 +244,17 @@ class Plugin extends ServerPlugin { ], 1); foreach ($result as $child) { - if (!isset($child[200]['{DAV:}resourcetype']) || !$child[200]['{DAV:}resourcetype']->is('{' . self::NS_CALDAV . '}calendar')) { + if (!isset($child[200]['{DAV:}resourcetype']) || !$child[200]['{DAV:}resourcetype']->is('{'.self::NS_CALDAV.'}calendar')) { // Node is either not a calendar continue; } if (isset($child[200]['{DAV:}share-access'])) { $shareAccess = $child[200]['{DAV:}share-access']->getValue(); - if ($shareAccess !== Sharing\Plugin::ACCESS_NOTSHARED && $shareAccess !== Sharing\Plugin::ACCESS_SHAREDOWNER) { + if (Sharing\Plugin::ACCESS_NOTSHARED !== $shareAccess && Sharing\Plugin::ACCESS_SHAREDOWNER !== $shareAccess) { // Node is a shared node, not owned by the relevant // user. continue; } - } if (!isset($child[200][$sccs]) || in_array('VEVENT', $child[200][$sccs]->getValue())) { // Either there is no supported-calendar-component-set @@ -271,82 +262,70 @@ class Plugin extends ServerPlugin { return new LocalHref($child['href']); } } - }); // The server currently reports every principal to be of type // 'INDIVIDUAL' - $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function() { - + $propFind->handle('{'.self::NS_CALDAV.'}calendar-user-type', function () { return 'INDIVIDUAL'; - }); - } // Mapping the old property to the new property. - $propFind->handle('{http://calendarserver.org/ns/}calendar-availability', function() use ($propFind, $node) { - - // In case it wasn't clear, the only difference is that we map the + $propFind->handle('{http://calendarserver.org/ns/}calendar-availability', function () use ($propFind, $node) { + // In case it wasn't clear, the only difference is that we map the // old property to a different namespace. - $availProp = '{' . self::NS_CALDAV . '}calendar-availability'; - $subPropFind = new PropFind( + $availProp = '{'.self::NS_CALDAV.'}calendar-availability'; + $subPropFind = new PropFind( $propFind->getPath(), [$availProp] ); - $this->server->getPropertiesByNode( + $this->server->getPropertiesByNode( $subPropFind, $node ); - $propFind->set( + $propFind->set( '{http://calendarserver.org/ns/}calendar-availability', $subPropFind->get($availProp), $subPropFind->getStatus($availProp) ); - }); - } /** * This method is called during property updates. * - * @param string $path + * @param string $path * @param PropPatch $propPatch - * @return void */ - function propPatch($path, PropPatch $propPatch) { - + public function propPatch($path, PropPatch $propPatch) + { // Mapping the old property to the new property. - $propPatch->handle('{http://calendarserver.org/ns/}calendar-availability', function($value) use ($path) { - - $availProp = '{' . self::NS_CALDAV . '}calendar-availability'; + $propPatch->handle('{http://calendarserver.org/ns/}calendar-availability', function ($value) use ($path) { + $availProp = '{'.self::NS_CALDAV.'}calendar-availability'; $subPropPatch = new PropPatch([$availProp => $value]); $this->server->emit('propPatch', [$path, $subPropPatch]); $subPropPatch->commit(); return $subPropPatch->getResult()[$availProp]; - }); - } /** * 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 + * @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 */ - function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) { - + public function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) + { if (!$this->scheduleReply($this->server->httpRequest)) { return; } @@ -370,17 +349,15 @@ class Plugin extends ServerPlugin { // Destroy circular references so PHP will GC the object. $oldObj->destroy(); } - } /** * This method is responsible for delivering the ITip message. * * @param ITip\Message $iTipMessage - * @return void */ - function deliver(ITip\Message $iTipMessage) { - + public 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'; @@ -391,7 +368,6 @@ class Plugin extends ServerPlugin { if (!$iTipMessage->significantChange && in_array($baseCode, ['3', '5'])) { $iTipMessage->scheduleStatus = null; } - } /** @@ -401,13 +377,14 @@ class Plugin extends ServerPlugin { * cancellations, and organizers get 'DECLINED' statuses. * * @param string $path - * @return void */ - function beforeUnbind($path) { - + public 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; + if ('MOVE' === $this->server->httpRequest->getMethod()) { + return; + } $node = $this->server->tree->getNodeForPath($path); @@ -429,7 +406,6 @@ class Plugin extends ServerPlugin { foreach ($messages as $message) { $this->deliver($message); } - } /** @@ -439,10 +415,9 @@ class Plugin extends ServerPlugin { * scheduling object. * * @param ITip\Message $iTipMessage - * @return void */ - function scheduleLocalDelivery(ITip\Message $iTipMessage) { - + public function scheduleLocalDelivery(ITip\Message $iTipMessage) + { $aclPlugin = $this->server->getPlugin('acl'); // Local delivery is not available if the ACL plugin is not loaded. @@ -450,11 +425,12 @@ class Plugin extends ServerPlugin { return; } - $caldavNS = '{' . self::NS_CALDAV . '}'; + $caldavNS = '{'.self::NS_CALDAV.'}'; $principalUri = $aclPlugin->getPrincipalByUri($iTipMessage->recipient); if (!$principalUri) { $iTipMessage->scheduleStatus = '3.7;Could not find principal.'; + return; } @@ -470,9 +446,9 @@ class Plugin extends ServerPlugin { $principalUri, [ '{DAV:}principal-URL', - $caldavNS . 'calendar-home-set', - $caldavNS . 'schedule-inbox-URL', - $caldavNS . 'schedule-default-calendar-URL', + $caldavNS.'calendar-home-set', + $caldavNS.'schedule-inbox-URL', + $caldavNS.'schedule-default-calendar-URL', '{http://sabredav.org/ns}email-address', ] ); @@ -480,31 +456,35 @@ class Plugin extends ServerPlugin { // Re-registering the ACL event $this->server->on('propFind', [$aclPlugin, 'propFind'], 20); - if (!isset($result[$caldavNS . 'schedule-inbox-URL'])) { + if (!isset($result[$caldavNS.'schedule-inbox-URL'])) { $iTipMessage->scheduleStatus = '5.2;Could not find local inbox'; + return; } - if (!isset($result[$caldavNS . 'calendar-home-set'])) { + 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'])) { + 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(); + $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') { + if ('REPLY' === $iTipMessage->method) { $privilege = 'schedule-deliver-reply'; } else { $privilege = 'schedule-deliver-invite'; } - if (!$aclPlugin->checkPrivileges($inboxPath, $caldavNS . $privilege, DAVACL\Plugin::R_PARENT, false)) { - $iTipMessage->scheduleStatus = '3.8;insufficient privileges: ' . $privilege . ' is required on the recipient schedule inbox.'; + if (!$aclPlugin->checkPrivileges($inboxPath, $caldavNS.$privilege, DAVACL\Plugin::R_PARENT, false)) { + $iTipMessage->scheduleStatus = '3.8;insufficient privileges: '.$privilege.' is required on the recipient schedule inbox.'; + return; } @@ -512,7 +492,7 @@ class Plugin extends ServerPlugin { // the users' calendars. $uid = $iTipMessage->uid; - $newFileName = 'sabredav-' . \Sabre\DAV\UUIDUtil::getUUID() . '.ics'; + $newFileName = 'sabredav-'.\Sabre\DAV\UUIDUtil::getUUID().'.ics'; $home = $this->server->tree->getNodeForPath($homePath); $inbox = $this->server->tree->getNodeForPath($inboxPath); @@ -524,7 +504,7 @@ class Plugin extends ServerPlugin { $result = $home->getCalendarObjectByUID($uid); if ($result) { // There was an existing object, we need to update probably. - $objectPath = $homePath . '/' . $result; + $objectPath = $homePath.'/'.$result; $objectNode = $this->server->tree->getNodeForPath($objectPath); $oldICalendarData = $objectNode->get(); $currentObject = Reader::read($oldICalendarData); @@ -545,6 +525,7 @@ class Plugin extends ServerPlugin { // 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; } @@ -558,7 +539,7 @@ class Plugin extends ServerPlugin { // 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') { + if ('REPLY' === $iTipMessage->method) { $this->processICalendarChange( $oldICalendarData, $newObject, @@ -569,7 +550,6 @@ class Plugin extends ServerPlugin { $objectNode->put($newObject->serialize()); } $iTipMessage->scheduleStatus = '1.2;Message delivered locally'; - } /** @@ -581,54 +561,53 @@ class Plugin extends ServerPlugin { * @param INode $node * @param array $supportedPrivilegeSet */ - function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) { - - $ns = '{' . self::NS_CALDAV . '}'; + public function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) + { + $ns = '{'.self::NS_CALDAV.'}'; if ($node instanceof IOutbox) { - $supportedPrivilegeSet[$ns . 'schedule-send'] = [ - 'abstract' => false, + $supportedPrivilegeSet[$ns.'schedule-send'] = [ + 'abstract' => false, 'aggregates' => [ - $ns . 'schedule-send-invite' => [ - 'abstract' => false, + $ns.'schedule-send-invite' => [ + 'abstract' => false, 'aggregates' => [], ], - $ns . 'schedule-send-reply' => [ - 'abstract' => false, + $ns.'schedule-send-reply' => [ + 'abstract' => false, 'aggregates' => [], ], - $ns . 'schedule-send-freebusy' => [ - 'abstract' => false, + $ns.'schedule-send-freebusy' => [ + 'abstract' => false, 'aggregates' => [], ], // Privilege from an earlier scheduling draft, but still // used by some clients. - $ns . 'schedule-post-vevent' => [ - 'abstract' => false, + $ns.'schedule-post-vevent' => [ + 'abstract' => false, 'aggregates' => [], ], - ] + ], ]; } if ($node instanceof IInbox) { - $supportedPrivilegeSet[$ns . 'schedule-deliver'] = [ - 'abstract' => false, + $supportedPrivilegeSet[$ns.'schedule-deliver'] = [ + 'abstract' => false, 'aggregates' => [ - $ns . 'schedule-deliver-invite' => [ - 'abstract' => false, + $ns.'schedule-deliver-invite' => [ + 'abstract' => false, 'aggregates' => [], ], - $ns . 'schedule-deliver-reply' => [ - 'abstract' => false, + $ns.'schedule-deliver-reply' => [ + 'abstract' => false, 'aggregates' => [], ], - $ns . 'schedule-query-freebusy' => [ - 'abstract' => false, + $ns.'schedule-query-freebusy' => [ + 'abstract' => false, 'aggregates' => [], ], - ] + ], ]; } - } /** @@ -642,22 +621,22 @@ class Plugin extends ServerPlugin { * 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 bool $modified A marker to indicate that the original object - * modified by this process. - * @return void + * @param VCalendar $newObject + * @param array $addresses + * @param array $ignore any addresses to not send messages to + * @param bool $modified a marker to indicate that the original object + * modified by this process */ - protected function processICalendarChange($oldObject = null, VCalendar $newObject, array $addresses, array $ignore = [], &$modified = false) { - + 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; + if ($messages) { + $modified = true; + } foreach ($messages as $message) { - if (in_array($message->recipient, $ignore)) { continue; } @@ -669,36 +648,32 @@ class Plugin extends ServerPlugin { $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(); + 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; } - 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'; + protected function getAddressesForPrincipal($principal) + { + $CUAS = '{'.self::NS_CALDAV.'}calendar-user-address-set'; $properties = $this->server->getProperties( $principal, @@ -707,12 +682,12 @@ class Plugin extends ServerPlugin { // If we can't find this information, we'll stop processing if (!isset($properties[$CUAS])) { - return; + return []; } $addresses = $properties[$CUAS]->getHrefs(); - return $addresses; + return $addresses; } /** @@ -726,20 +701,19 @@ class Plugin extends ServerPlugin { * extensions, but iCal depends on a feature from that spec, so we * implement it. * - * @param IOutbox $outboxNode - * @param RequestInterface $request + * @param IOutbox $outboxNode + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function outboxRequest(IOutbox $outboxNode, RequestInterface $request, ResponseInterface $response) { - + public 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()); + 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 @@ -747,7 +721,7 @@ class Plugin extends ServerPlugin { // this is. $componentType = null; foreach ($vObject->getComponents() as $component) { - if ($component->name !== 'VTIMEZONE') { + if ('VTIMEZONE' !== $component->name) { $componentType = $component->name; break; } @@ -757,7 +731,7 @@ class Plugin extends ServerPlugin { } // Validating the METHOD - $method = strtoupper((string)$vObject->METHOD); + $method = strtoupper((string) $vObject->METHOD); if (!$method) { throw new BadRequest('A METHOD property must be specified in iTIP messages'); } @@ -768,46 +742,42 @@ class Plugin extends ServerPlugin { $acl = $this->server->getPlugin('acl'); - if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') { - - $acl && $acl->checkPrivileges($outboxPath, '{' . self::NS_CALDAV . '}schedule-send-freebusy'); + if ('VFREEBUSY' === $componentType && 'REQUEST' === $method) { + $acl && $acl->checkPrivileges($outboxPath, '{'.self::NS_CALDAV.'}schedule-send-freebusy'); $this->handleFreeBusyRequest($outboxNode, $vObject, $request, $response); // Destroy circular references so PHP can GC the object. $vObject->destroy(); unset($vObject); - } 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 IOutbox $outbox * @param VObject\Component $vObject - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return string */ - protected function handleFreeBusyRequest(IOutbox $outbox, VObject\Component $vObject, RequestInterface $request, ResponseInterface $response) { - + protected function handleFreeBusyRequest(IOutbox $outbox, VObject\Component $vObject, RequestInterface $request, ResponseInterface $response) + { $vFreeBusy = $vObject->VFREEBUSY; $organizer = $vFreeBusy->ORGANIZER; - $organizer = (string)$organizer; + $organizer = (string) $organizer; // Validating if the organizer matches the owner of the inbox. $owner = $outbox->getOwner(); - $caldavNS = '{' . self::NS_CALDAV . '}'; + $caldavNS = '{'.self::NS_CALDAV.'}'; - $uas = $caldavNS . 'calendar-user-address-set'; + $uas = $caldavNS.'calendar-user-address-set'; $props = $this->server->getProperties($owner, [$uas]); if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) { @@ -820,10 +790,9 @@ class Plugin extends ServerPlugin { $attendees = []; foreach ($vFreeBusy->ATTENDEE as $attendee) { - $attendees[] = (string)$attendee; + $attendees[] = (string) $attendee; } - if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) { throw new BadRequest('DTSTART and DTEND must both be specified'); } @@ -840,9 +809,7 @@ class Plugin extends ServerPlugin { $dom->formatOutput = true; $scheduleResponse = $dom->createElement('cal:schedule-response'); foreach ($this->server->xml->namespaceMap as $namespace => $prefix) { - - $scheduleResponse->setAttribute('xmlns:' . $prefix, $namespace); - + $scheduleResponse->setAttribute('xmlns:'.$prefix, $namespace); } $dom->appendChild($scheduleResponse); @@ -861,11 +828,9 @@ class Plugin extends ServerPlugin { $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); } @@ -873,12 +838,11 @@ class Plugin extends ServerPlugin { $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: + * data is an array containing the following properties:. * * calendar-data : A VFREEBUSY VObject * request-status : an iTip status code. @@ -888,54 +852,56 @@ class Plugin extends ServerPlugin { * * 2.0;description * * 3.7;description * - * @param string $email address + * @param string $email address * @param \DateTimeInterface $start * @param \DateTimeInterface $end - * @param VObject\Component $request + * @param VObject\Component $request + * * @return array */ - protected function getFreeBusyForEmail($email, \DateTimeInterface $start, \DateTimeInterface $end, VObject\Component $request) { - - $caldavNS = '{' . self::NS_CALDAV . '}'; + protected function getFreeBusyForEmail($email, \DateTimeInterface $start, \DateTimeInterface $end, VObject\Component $request) + { + $caldavNS = '{'.self::NS_CALDAV.'}'; $aclPlugin = $this->server->getPlugin('acl'); - if (substr($email, 0, 7) === 'mailto:') $email = substr($email, 7); + if ('mailto:' === substr($email, 0, 7)) { + $email = substr($email, 7); + } $result = $aclPlugin->principalSearch( ['{http://sabredav.org/ns}email-address' => $email], [ '{DAV:}principal-URL', - $caldavNS . 'calendar-home-set', - $caldavNS . 'schedule-inbox-URL', + $caldavNS.'calendar-home-set', + $caldavNS.'schedule-inbox-URL', '{http://sabredav.org/ns}email-address', - ] ); if (!count($result)) { return [ 'request-status' => '3.7;Could not find principal', - 'href' => 'mailto:' . $email, + 'href' => 'mailto:'.$email, ]; } - if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { + if (!isset($result[0][200][$caldavNS.'calendar-home-set'])) { return [ 'request-status' => '3.7;No calendar-home-set property found', - 'href' => 'mailto:' . $email, + 'href' => 'mailto:'.$email, ]; } - if (!isset($result[0][200][$caldavNS . 'schedule-inbox-URL'])) { + if (!isset($result[0][200][$caldavNS.'schedule-inbox-URL'])) { return [ 'request-status' => '3.7;No schedule-inbox-URL property found', - 'href' => 'mailto:' . $email, + 'href' => 'mailto:'.$email, ]; } - $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); - $inboxUrl = $result[0][200][$caldavNS . 'schedule-inbox-URL']->getHref(); + $homeSet = $result[0][200][$caldavNS.'calendar-home-set']->getHref(); + $inboxUrl = $result[0][200][$caldavNS.'schedule-inbox-URL']->getHref(); // Do we have permission? - $aclPlugin->checkPrivileges($inboxUrl, $caldavNS . 'schedule-query-freebusy'); + $aclPlugin->checkPrivileges($inboxUrl, $caldavNS.'schedule-query-freebusy'); // Grabbing the calendar list $objects = []; @@ -946,11 +912,11 @@ class Plugin extends ServerPlugin { continue; } - $sct = $caldavNS . 'schedule-calendar-transp'; - $ctz = $caldavNS . 'calendar-timezone'; + $sct = $caldavNS.'schedule-calendar-transp'; + $ctz = $caldavNS.'calendar-timezone'; $props = $node->getProperties([$sct, $ctz]); - if (isset($props[$sct]) && $props[$sct]->getValue() == ScheduleCalendarTransp::TRANSPARENT) { + if (isset($props[$sct]) && ScheduleCalendarTransp::TRANSPARENT == $props[$sct]->getValue()) { // If a calendar is marked as 'transparent', it means we must // ignore it for free-busy purposes. continue; @@ -962,41 +928,40 @@ class Plugin extends ServerPlugin { // Destroy circular references so PHP can garbage collect the object. $vtimezoneObj->destroy(); - } // Getting the list of object uris within the time-range $urls = $node->calendarQuery([ - 'name' => 'VCALENDAR', + 'name' => 'VCALENDAR', 'comp-filters' => [ [ - 'name' => 'VEVENT', - 'comp-filters' => [], - 'prop-filters' => [], + 'name' => 'VEVENT', + 'comp-filters' => [], + 'prop-filters' => [], 'is-not-defined' => false, - 'time-range' => [ + 'time-range' => [ 'start' => $start, - 'end' => $end, + 'end' => $end, ], ], ], - 'prop-filters' => [], + 'prop-filters' => [], 'is-not-defined' => false, - 'time-range' => null, + 'time-range' => null, ]); - $calObjects = array_map(function($url) use ($node) { + $calObjects = array_map(function ($url) use ($node) { $obj = $node->getChild($url)->get(); + return $obj; }, $urls); $objects = array_merge($objects, $calObjects); - } $inboxProps = $this->server->getProperties( $inboxUrl, - $caldavNS . 'calendar-availability' + $caldavNS.'calendar-availability' ); $vcalendar = new VObject\Component\VCalendar(); @@ -1011,21 +976,21 @@ class Plugin extends ServerPlugin { if ($inboxProps) { $generator->setVAvailability( VObject\Reader::read( - $inboxProps[$caldavNS . 'calendar-availability'] + $inboxProps[$caldavNS.'calendar-availability'] ) ); } $result = $generator->getResult(); - $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; - $vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID; + $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:'.$email; + $vcalendar->VFREEBUSY->UID = (string) $request->VFREEBUSY->UID; $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; return [ - 'calendar-data' => $result, + 'calendar-data' => $result, 'request-status' => '2.0;Success', - 'href' => 'mailto:' . $email, + 'href' => 'mailto:'.$email, ]; } @@ -1034,13 +999,14 @@ class Plugin extends ServerPlugin { * and returns false if it's 'F', otherwise true. * * @param RequestInterface $request + * * @return bool */ - private function scheduleReply(RequestInterface $request) { - + private function scheduleReply(RequestInterface $request) + { $scheduleReply = $request->getHeader('Schedule-Reply'); - return $scheduleReply !== 'F'; + return 'F' !== $scheduleReply; } /** @@ -1054,13 +1020,12 @@ class Plugin extends ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds calendar-auto-schedule, as defined in rfc6638', - 'link' => 'http://sabre.io/dav/scheduling/', + 'link' => 'http://sabre.io/dav/scheduling/', ]; - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php index 0cd05a965c3..d34c9232708 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Schedule/SchedulingObject.php @@ -1,35 +1,23 @@ caldavBackend = $caldavBackend; + public function __construct(Backend\SchedulingSupport $caldavBackend, array $objectData) + { + parent::__construct($caldavBackend, [], $objectData); if (!isset($objectData['uri'])) { throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property'); } - - $this->objectData = $objectData; - } /** - * Returns the ICalendar-formatted object + * Returns the ICalendar-formatted object. * * @return string */ - function get() { - + public function get() + { // Pre-populating the 'calendardata' is optional, if we don't have it // already we fetch it from the backend. if (!isset($this->objectData['calendardata'])) { $this->objectData = $this->caldavBackend->getSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']); } - return $this->objectData['calendardata']; + return $this->objectData['calendardata']; } /** - * Updates the ICalendar-formatted object + * Updates the ICalendar-formatted object. * * @param string|resource $calendarData + * * @return string */ - function put($calendarData) { - + public function put($calendarData) + { throw new MethodNotAllowed('Updating scheduling objects is not supported'); - } /** - * Deletes the scheduling message - * - * @return void + * Deletes the scheduling message. */ - function delete() { - + public function delete() + { $this->caldavBackend->deleteSchedulingObject($this->objectData['principaluri'], $this->objectData['uri']); - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->objectData['principaluri']; - } - /** * Returns a list of ACE's for this node. * @@ -122,8 +102,8 @@ class SchedulingObject extends \Sabre\CalDAV\CalendarObject implements IScheduli * * @return array */ - function getACL() { - + public function getACL() + { // An alternative acl may be specified in the object data. // @@ -140,16 +120,14 @@ class SchedulingObject extends \Sabre\CalDAV\CalendarObject implements IScheduli ], [ 'privilege' => '{DAV:}all', - 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-write', + 'principal' => $this->objectData['principaluri'].'/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->objectData['principaluri'] . '/calendar-proxy-read', + 'principal' => $this->objectData['principaluri'].'/calendar-proxy-read', 'protected' => true, ], ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharedCalendar.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharedCalendar.php index 7a77616e353..818392f57c5 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharedCalendar.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharedCalendar.php @@ -1,5 +1,7 @@ calendarInfo['share-access']) ? $this->calendarInfo['share-access'] : SPlugin::ACCESS_NOTSHARED; - } /** @@ -38,10 +39,9 @@ class SharedCalendar extends Calendar implements ISharedCalendar { * * @return string */ - function getShareResourceUri() { - + public function getShareResourceUri() + { return $this->calendarInfo['share-resource-uri']; - } /** @@ -50,12 +50,10 @@ class SharedCalendar extends Calendar implements ISharedCalendar { * Every item must be a Sharee object. * * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees - * @return void */ - function updateInvites(array $sharees) { - + public function updateInvites(array $sharees) + { $this->caldavBackend->updateInvites($this->calendarInfo['id'], $sharees); - } /** @@ -74,10 +72,9 @@ class SharedCalendar extends Calendar implements ISharedCalendar { * * @return \Sabre\DAV\Xml\Element\Sharee[] */ - function getInvites() { - + public function getInvites() + { return $this->caldavBackend->getInvites($this->calendarInfo['id']); - } /** @@ -87,12 +84,10 @@ class SharedCalendar extends Calendar implements ISharedCalendar { * subscribable calendar. * * @param bool $value - * @return void */ - function setPublishStatus($value) { - + public function setPublishStatus($value) + { $this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value); - } /** @@ -107,13 +102,13 @@ class SharedCalendar extends Calendar implements ISharedCalendar { * * @return array */ - function getACL() { - + public function getACL() + { $acl = []; switch ($this->getShareAccess()) { - case SPlugin::ACCESS_NOTSHARED : - case SPlugin::ACCESS_SHAREDOWNER : + case SPlugin::ACCESS_NOTSHARED: + case SPlugin::ACCESS_SHAREDOWNER: $acl[] = [ 'privilege' => '{DAV:}share', 'principal' => $this->calendarInfo['principaluri'], @@ -121,76 +116,10 @@ class SharedCalendar extends Calendar implements ISharedCalendar { ]; $acl[] = [ 'privilege' => '{DAV:}share', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write', 'protected' => true, ]; - // No break intentional! - case SPlugin::ACCESS_READWRITE : - $acl[] = [ - 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ]; - $acl[] = [ - 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', - 'protected' => true, - ]; - // No break intentional! - case SPlugin::ACCESS_READ : - $acl[] = [ - 'privilege' => '{DAV:}write-properties', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ]; - $acl[] = [ - 'privilege' => '{DAV:}write-properties', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', - 'protected' => true, - ]; - $acl[] = [ - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'], - 'protected' => true, - ]; - $acl[] = [ - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', - 'protected' => true, - ]; - $acl[] = [ - 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', - 'protected' => true, - ]; - $acl[] = [ - 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy', - 'principal' => '{DAV:}authenticated', - 'protected' => true, - ]; - break; - } - 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 = []; - - switch ($this->getShareAccess()) { - case SPlugin::ACCESS_NOTSHARED : - // No break intentional - case SPlugin::ACCESS_SHAREDOWNER : - // No break intentional + // no break intentional! case SPlugin::ACCESS_READWRITE: $acl[] = [ 'privilege' => '{DAV:}write', @@ -199,10 +128,73 @@ class SharedCalendar extends Calendar implements ISharedCalendar { ]; $acl[] = [ 'privilege' => '{DAV:}write', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write', 'protected' => true, ]; - // No break intentional + // no break intentional! + case SPlugin::ACCESS_READ: + $acl[] = [ + 'privilege' => '{DAV:}write-properties', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}write-properties', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write', + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-read', + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write', + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{'.Plugin::NS_CALDAV.'}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ]; + break; + } + + 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 + */ + public function getChildACL() + { + $acl = []; + + switch ($this->getShareAccess()) { + case SPlugin::ACCESS_NOTSHARED: + case SPlugin::ACCESS_SHAREDOWNER: + case SPlugin::ACCESS_READWRITE: + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'], + 'protected' => true, + ]; + $acl[] = [ + 'privilege' => '{DAV:}write', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write', + 'protected' => true, + ]; + // no break intentional case SPlugin::ACCESS_READ: $acl[] = [ 'privilege' => '{DAV:}read', @@ -211,19 +203,17 @@ class SharedCalendar extends Calendar implements ISharedCalendar { ]; $acl[] = [ 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-write', 'protected' => true, ]; $acl[] = [ 'privilege' => '{DAV:}read', - 'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read', + 'principal' => $this->calendarInfo['principaluri'].'/calendar-proxy-read', 'protected' => true, ]; break; } return $acl; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharingPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharingPlugin.php index 5cce79678be..be8c46a9eeb 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharingPlugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/SharingPlugin.php @@ -1,5 +1,7 @@ server = $server; if (is_null($this->server->getPlugin('sharing'))) { @@ -81,19 +80,18 @@ class SharingPlugin extends DAV\ServerPlugin { array_push( $this->server->protectedProperties, - '{' . Plugin::NS_CALENDARSERVER . '}invite', - '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', - '{' . Plugin::NS_CALENDARSERVER . '}shared-url' + '{'.Plugin::NS_CALENDARSERVER.'}invite', + '{'.Plugin::NS_CALENDARSERVER.'}allowed-sharing-modes', + '{'.Plugin::NS_CALENDARSERVER.'}shared-url' ); - $this->server->xml->elementMap['{' . Plugin::NS_CALENDARSERVER . '}share'] = 'Sabre\\CalDAV\\Xml\\Request\\Share'; - $this->server->xml->elementMap['{' . Plugin::NS_CALENDARSERVER . '}invite-reply'] = 'Sabre\\CalDAV\\Xml\\Request\\InviteReply'; - - $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->server->xml->elementMap['{'.Plugin::NS_CALENDARSERVER.'}share'] = 'Sabre\\CalDAV\\Xml\\Request\\Share'; + $this->server->xml->elementMap['{'.Plugin::NS_CALENDARSERVER.'}invite-reply'] = 'Sabre\\CalDAV\\Xml\\Request\\InviteReply'; + $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']); } /** @@ -103,47 +101,17 @@ class SharingPlugin extends DAV\ServerPlugin { * This allows us to inject any properties early. * * @param DAV\PropFind $propFind - * @param DAV\INode $node - * @return void + * @param DAV\INode $node */ - function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) { - + public function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) + { if ($node instanceof ISharedCalendar) { - - $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']; - } - - } - + $propFind->handle('{'.Plugin::NS_CALENDARSERVER.'}invite', function () use ($node) { return new Xml\Property\Invite( - $node->getInvites(), - $ownerInfo + $node->getInvites() ); - }); - } - } /** @@ -152,31 +120,27 @@ class SharingPlugin extends DAV\ServerPlugin { * have been shared. * * @param DAV\PropFind $propFind - * @param DAV\INode $node - * @return void + * @param DAV\INode $node */ - function propFindLate(DAV\PropFind $propFind, DAV\INode $node) { - + public function propFindLate(DAV\PropFind $propFind, DAV\INode $node) + { if ($node instanceof ISharedCalendar) { $shareAccess = $node->getShareAccess(); if ($rt = $propFind->get('{DAV:}resourcetype')) { switch ($shareAccess) { - case \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER : - $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared-owner'); + case \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER: + $rt->add('{'.Plugin::NS_CALENDARSERVER.'}shared-owner'); break; - case \Sabre\DAV\Sharing\Plugin::ACCESS_READ : - case \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE : - $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared'); + case \Sabre\DAV\Sharing\Plugin::ACCESS_READ: + case \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE: + $rt->add('{'.Plugin::NS_CALENDARSERVER.'}shared'); break; - } } - $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', function() { + $propFind->handle('{'.Plugin::NS_CALENDARSERVER.'}allowed-sharing-modes', function () { return new Xml\Property\AllowedSharingModes(true, false); }); - } - } /** @@ -190,20 +154,21 @@ class SharingPlugin extends DAV\ServerPlugin { * 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 string $path * @param DAV\PropPatch $propPatch - * @return void */ - function propPatch($path, DAV\PropPatch $propPatch) { - + public function propPatch($path, DAV\PropPatch $propPatch) + { $node = $this->server->tree->getNodeForPath($path); - if (!$node instanceof ISharedCalendar) + if (!$node instanceof ISharedCalendar) { return; + } - if ($node->getShareAccess() === \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER || $node->getShareAccess() === \Sabre\DAV\Sharing\Plugin::ACCESS_NOTSHARED) { - - $propPatch->handle('{DAV:}resourcetype', function($value) use ($node) { - if ($value->is('{' . Plugin::NS_CALENDARSERVER . '}shared-owner')) return false; + if (\Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER === $node->getShareAccess() || \Sabre\DAV\Sharing\Plugin::ACCESS_NOTSHARED === $node->getShareAccess()) { + $propPatch->handle('{DAV:}resourcetype', function ($value) use ($node) { + if ($value->is('{'.Plugin::NS_CALENDARSERVER.'}shared-owner')) { + return false; + } $shares = $node->getInvites(); foreach ($shares as $share) { $share->access = DAV\Sharing\Plugin::ACCESS_NOACCESS; @@ -211,28 +176,30 @@ class SharingPlugin extends DAV\ServerPlugin { $node->updateInvites($shares); return true; - }); - } - } /** * We intercept this to handle POST requests on calendars. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return null|bool + * + * @return bool|null */ - function httpPost(RequestInterface $request, ResponseInterface $response) { - + public 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) + if (null === $contentType) { return; + } + if (false === strpos($contentType, 'application/xml') && false === strpos($contentType, 'text/xml')) { + return; + } // Making sure the node exists try { @@ -255,10 +222,9 @@ class SharingPlugin extends DAV\ServerPlugin { $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType); switch ($documentType) { - // Both the DAV:share-resource and CALENDARSERVER:share requests // behave identically. - case '{' . Plugin::NS_CALENDARSERVER . '}share' : + case '{'.Plugin::NS_CALENDARSERVER.'}share': $sharingPlugin = $this->server->getPlugin('sharing'); $sharingPlugin->shareResource($path, $message->sharees); @@ -273,7 +239,7 @@ class SharingPlugin extends DAV\ServerPlugin { // The invite-reply document is sent when the user replies to an // invitation of a calendar share. - case '{' . Plugin::NS_CALENDARSERVER . '}invite-reply' : + case '{'.Plugin::NS_CALENDARSERVER.'}invite-reply': // This only works on the calendar-home-root node. if (!$node instanceof CalendarHome) { @@ -304,20 +270,20 @@ class SharingPlugin extends DAV\ServerPlugin { if ($url) { $writer = $this->server->xml->getWriter(); + $writer->contextUri = $request->getUrl(); $writer->openMemory(); $writer->startDocument(); - $writer->startElement('{' . Plugin::NS_CALENDARSERVER . '}shared-as'); + $writer->startElement('{'.Plugin::NS_CALENDARSERVER.'}shared-as'); $writer->write(new LocalHref($url)); $writer->endElement(); $response->setHeader('Content-Type', 'application/xml'); $response->setBody($writer->outputMemory()); - } // Breaking the event chain return false; - case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar' : + case '{'.Plugin::NS_CALENDARSERVER.'}publish-calendar': // We can only deal with IShareableCalendar objects if (!$node instanceof ISharedCalendar) { @@ -345,7 +311,7 @@ class SharingPlugin extends DAV\ServerPlugin { // Breaking the event chain return false; - case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar' : + case '{'.Plugin::NS_CALENDARSERVER.'}unpublish-calendar': // We can only deal with IShareableCalendar objects if (!$node instanceof ISharedCalendar) { @@ -371,11 +337,7 @@ class SharingPlugin extends DAV\ServerPlugin { // Breaking the event chain return false; - } - - - } /** @@ -389,13 +351,12 @@ class SharingPlugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds support for caldav-sharing.', - 'link' => 'http://sabre.io/dav/caldav-sharing/', + 'link' => 'http://sabre.io/dav/caldav-sharing/', ]; - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php index 7ba259c7b68..e83082c52bd 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/ISubscription.php @@ -1,12 +1,14 @@ resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] = '{http://calendarserver.org/ns/}subscribed'; @@ -39,7 +40,6 @@ class Plugin extends ServerPlugin { 'Sabre\\DAV\\Xml\\Property\\Href'; $server->on('propFind', [$this, 'propFind'], 150); - } /** @@ -50,21 +50,19 @@ class Plugin extends ServerPlugin { * * @return array */ - function getFeatures() { - + public function getFeatures() + { return ['calendarserver-subscribed']; - } /** * Triggered after properties have been fetched. * * @param PropFind $propFind - * @param INode $node - * @return void + * @param INode $node */ - function propFind(PropFind $propFind, INode $node) { - + public 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 = [ @@ -74,13 +72,10 @@ class Plugin extends ServerPlugin { ]; foreach ($props as $prop) { - - if ($propFind->getStatus($prop) === 200) { + if (200 === $propFind->getStatus($prop)) { $propFind->set($prop, '', 200); } - } - } /** @@ -91,10 +86,9 @@ class Plugin extends ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'subscriptions'; - } /** @@ -108,13 +102,12 @@ class Plugin extends ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'This plugin allows users to store iCalendar subscriptions in their calendar-home.', - 'link' => null, + 'link' => null, ]; - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php index 6a1851ed868..0b0282abe6d 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php @@ -1,5 +1,7 @@ caldavBackend = $caldavBackend; $this->subscriptionInfo = $subscriptionInfo; @@ -56,10 +58,9 @@ class Subscription extends Collection implements ISubscription, IACL { foreach ($required as $r) { if (!isset($subscriptionInfo[$r])) { - throw new \InvalidArgumentException('The ' . $r . ' field is required when creating a subscription node'); + throw new \InvalidArgumentException('The '.$r.' field is required when creating a subscription node'); } } - } /** @@ -69,47 +70,41 @@ class Subscription extends Collection implements ISubscription, IACL { * * @return string */ - function getName() { - + public function getName() + { return $this->subscriptionInfo['uri']; - } /** - * Returns the last modification time + * Returns the last modification time. * * @return int */ - function getLastModified() { - + public function getLastModified() + { if (isset($this->subscriptionInfo['lastmodified'])) { return $this->subscriptionInfo['lastmodified']; } - } /** - * Deletes the current node - * - * @return void + * Deletes the current node. */ - function delete() { - + public function delete() + { $this->caldavBackend->deleteSubscription( $this->subscriptionInfo['id'] ); - } /** - * Returns an array with all the child nodes + * Returns an array with all the child nodes. * * @return \Sabre\DAV\INode[] */ - function getChildren() { - + public function getChildren() + { return []; - } /** @@ -122,15 +117,13 @@ class Subscription extends Collection implements ISubscription, IACL { * Read the PropPatch documentation for more information. * * @param PropPatch $propPatch - * @return void */ - function propPatch(PropPatch $propPatch) { - + public function propPatch(PropPatch $propPatch) + { return $this->caldavBackend->updateSubscription( $this->subscriptionInfo['id'], $propPatch ); - } /** @@ -146,29 +139,27 @@ class Subscription extends Collection implements ISubscription, IACL { * The Server class will filter out the extra. * * @param array $properties + * * @return array */ - function getProperties($properties) { - + public function getProperties($properties) + { $r = []; foreach ($properties as $prop) { - switch ($prop) { - case '{http://calendarserver.org/ns/}source' : + case '{http://calendarserver.org/ns/}source': $r[$prop] = new Href($this->subscriptionInfo['source']); break; - default : + default: if (array_key_exists($prop, $this->subscriptionInfo)) { $r[$prop] = $this->subscriptionInfo[$prop]; } break; } - } return $r; - } /** @@ -178,10 +169,9 @@ class Subscription extends Collection implements ISubscription, IACL { * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->subscriptionInfo['principaluri']; - } /** @@ -196,8 +186,8 @@ class Subscription extends Collection implements ISubscription, IACL { * * @return array */ - function getACL() { - + public function getACL() + { return [ [ 'privilege' => '{DAV:}all', @@ -206,16 +196,14 @@ class Subscription extends Collection implements ISubscription, IACL { ], [ 'privilege' => '{DAV:}all', - 'principal' => $this->getOwner() . '/calendar-proxy-write', + 'principal' => $this->getOwner().'/calendar-proxy-write', 'protected' => true, ], [ 'privilege' => '{DAV:}read', - 'principal' => $this->getOwner() . '/calendar-proxy-read', + 'principal' => $this->getOwner().'/calendar-proxy-read', 'protected' => true, - ] + ], ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php index 9669be304e1..0d53aeda30f 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CalendarData.php @@ -1,5 +1,7 @@ $reader->getAttribute('content-type') ?: 'text/calendar', - 'version' => $reader->getAttribute('version') ?: '2.0', + 'version' => $reader->getAttribute('version') ?: '2.0', ]; - $elems = (array)$reader->parseInnerTree(); + $elems = (array) $reader->parseInnerTree(); foreach ($elems as $elem) { - switch ($elem['name']) { - case '{' . Plugin::NS_CALDAV . '}expand' : + case '{'.Plugin::NS_CALDAV.'}expand': $result['expand'] = [ 'start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, - 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, + 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, ]; if (!$result['expand']['start'] || !$result['expand']['end']) { @@ -74,11 +76,8 @@ class CalendarData implements XmlDeserializable { } break; } - } return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php index c21ede66b5c..832346eea54 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/CompFilter.php @@ -1,5 +1,7 @@ null, + 'name' => null, 'is-not-defined' => false, - 'comp-filters' => [], - 'prop-filters' => [], - 'time-range' => false, + 'comp-filters' => [], + 'prop-filters' => [], + 'time-range' => false, ]; $att = $reader->parseAttributes(); @@ -60,38 +63,34 @@ class CompFilter implements XmlDeserializable { $elems = $reader->parseInnerTree(); - if (is_array($elems)) foreach ($elems as $elem) { - - switch ($elem['name']) { - - case '{' . Plugin::NS_CALDAV . '}comp-filter' : + if (is_array($elems)) { + foreach ($elems as $elem) { + switch ($elem['name']) { + case '{'.Plugin::NS_CALDAV.'}comp-filter': $result['comp-filters'][] = $elem['value']; break; - case '{' . Plugin::NS_CALDAV . '}prop-filter' : + case '{'.Plugin::NS_CALDAV.'}prop-filter': $result['prop-filters'][] = $elem['value']; break; - case '{' . Plugin::NS_CALDAV . '}is-not-defined' : + case '{'.Plugin::NS_CALDAV.'}is-not-defined': $result['is-not-defined'] = true; break; - case '{' . Plugin::NS_CALDAV . '}time-range' : - if ($result['name'] === 'VCALENDAR') { + case '{'.Plugin::NS_CALDAV.'}time-range': + if ('VCALENDAR' === $result['name']) { throw new BadRequest('You cannot add time-range filters on the VCALENDAR component'); } $result['time-range'] = [ 'start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, - 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, + 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, ]; if ($result['time-range']['start'] && $result['time-range']['end'] && $result['time-range']['end'] <= $result['time-range']['start']) { throw new BadRequest('The end-date must be larger than the start-date'); } break; - } - + } } return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php index bf422cf0545..ec9ff753c7d 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/ParamFilter.php @@ -1,5 +1,7 @@ null, + 'name' => null, 'is-not-defined' => false, - 'text-match' => null, + 'text-match' => null, ]; $att = $reader->parseAttributes(); @@ -56,27 +59,23 @@ class ParamFilter implements XmlDeserializable { $elems = $reader->parseInnerTree(); - if (is_array($elems)) foreach ($elems as $elem) { - - switch ($elem['name']) { - - case '{' . Plugin::NS_CALDAV . '}is-not-defined' : + if (is_array($elems)) { + foreach ($elems as $elem) { + switch ($elem['name']) { + case '{'.Plugin::NS_CALDAV.'}is-not-defined': $result['is-not-defined'] = true; break; - case '{' . Plugin::NS_CALDAV . '}text-match' : + case '{'.Plugin::NS_CALDAV.'}text-match': $result['text-match'] = [ - 'negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', - 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;ascii-casemap', - 'value' => $elem['value'], + 'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'], + 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;ascii-casemap', + 'value' => $elem['value'], ]; break; - } - + } } return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php index db9207295a2..f4600574e50 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Filter/PropFilter.php @@ -1,5 +1,7 @@ null, + 'name' => null, 'is-not-defined' => false, - 'param-filters' => [], - 'text-match' => null, - 'time-range' => false, + 'param-filters' => [], + 'text-match' => null, + 'time-range' => false, ]; $att = $reader->parseAttributes(); @@ -60,39 +63,35 @@ class PropFilter implements XmlDeserializable { $elems = $reader->parseInnerTree(); - if (is_array($elems)) foreach ($elems as $elem) { - - switch ($elem['name']) { - - case '{' . Plugin::NS_CALDAV . '}param-filter' : + if (is_array($elems)) { + foreach ($elems as $elem) { + switch ($elem['name']) { + case '{'.Plugin::NS_CALDAV.'}param-filter': $result['param-filters'][] = $elem['value']; break; - case '{' . Plugin::NS_CALDAV . '}is-not-defined' : + case '{'.Plugin::NS_CALDAV.'}is-not-defined': $result['is-not-defined'] = true; break; - case '{' . Plugin::NS_CALDAV . '}time-range' : + case '{'.Plugin::NS_CALDAV.'}time-range': $result['time-range'] = [ 'start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, - 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, + 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null, ]; if ($result['time-range']['start'] && $result['time-range']['end'] && $result['time-range']['end'] <= $result['time-range']['start']) { throw new BadRequest('The end-date must be larger than the start-date'); } break; - case '{' . Plugin::NS_CALDAV . '}text-match' : + case '{'.Plugin::NS_CALDAV.'}text-match': $result['text-match'] = [ - 'negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', - 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;ascii-casemap', - 'value' => $elem['value'], + 'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'], + 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;ascii-casemap', + 'value' => $elem['value'], ]; break; - } - + } } return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php index 92a9ac7b715..926656674bd 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php @@ -1,5 +1,7 @@ $value) { if (!property_exists($this, $key)) { - throw new \InvalidArgumentException('Unknown option: ' . $key); + throw new \InvalidArgumentException('Unknown option: '.$key); } $this->$key = $value; } - } /** @@ -182,12 +183,10 @@ class Invite implements NotificationInterface { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - - $writer->writeElement('{' . CalDAV\Plugin::NS_CALENDARSERVER . '}invite-notification'); - + public function xmlSerialize(Writer $writer) + { + $writer->writeElement('{'.CalDAV\Plugin::NS_CALENDARSERVER.'}invite-notification'); } /** @@ -195,95 +194,90 @@ class Invite implements NotificationInterface { * response body. * * @param Writer $writer - * @return void */ - function xmlSerializeFull(Writer $writer) { + public function xmlSerializeFull(Writer $writer) + { + $cs = '{'.CalDAV\Plugin::NS_CALENDARSERVER.'}'; - $cs = '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}'; + $this->dtStamp->setTimezone(new \DateTimeZone('GMT')); + $writer->writeElement($cs.'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z')); - $this->dtStamp->setTimezone(new \DateTimezone('GMT')); - $writer->writeElement($cs . 'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z')); + $writer->startElement($cs.'invite-notification'); - $writer->startElement($cs . 'invite-notification'); - - $writer->writeElement($cs . 'uid', $this->id); + $writer->writeElement($cs.'uid', $this->id); $writer->writeElement('{DAV:}href', $this->href); switch ($this->type) { - - case DAV\Sharing\Plugin::INVITE_ACCEPTED : - $writer->writeElement($cs . 'invite-accepted'); + case DAV\Sharing\Plugin::INVITE_ACCEPTED: + $writer->writeElement($cs.'invite-accepted'); break; - case DAV\Sharing\Plugin::INVITE_NORESPONSE : - $writer->writeElement($cs . 'invite-noresponse'); + case DAV\Sharing\Plugin::INVITE_NORESPONSE: + $writer->writeElement($cs.'invite-noresponse'); break; - } - $writer->writeElement($cs . 'hosturl', [ - '{DAV:}href' => $writer->contextUri . $this->hostUrl + $writer->writeElement($cs.'hosturl', [ + '{DAV:}href' => $writer->contextUri.$this->hostUrl, ]); if ($this->summary) { - $writer->writeElement($cs . 'summary', $this->summary); + $writer->writeElement($cs.'summary', $this->summary); } - $writer->startElement($cs . 'access'); + $writer->startElement($cs.'access'); if ($this->readOnly) { - $writer->writeElement($cs . 'read'); + $writer->writeElement($cs.'read'); } else { - $writer->writeElement($cs . 'read-write'); + $writer->writeElement($cs.'read-write'); } $writer->endElement(); // access - $writer->startElement($cs . 'organizer'); + $writer->startElement($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:') { + if ('mailto:' === strtolower(substr($this->organizer, 0, 7))) { $writer->writeElement('{DAV:}href', $this->organizer); } else { - $writer->writeElement('{DAV:}href', $writer->contextUri . $this->organizer); + $writer->writeElement('{DAV:}href', $writer->contextUri.$this->organizer); } if ($this->commonName) { - $writer->writeElement($cs . 'common-name', $this->commonName); + $writer->writeElement($cs.'common-name', $this->commonName); } if ($this->firstName) { - $writer->writeElement($cs . 'first-name', $this->firstName); + $writer->writeElement($cs.'first-name', $this->firstName); } if ($this->lastName) { - $writer->writeElement($cs . 'last-name', $this->lastName); + $writer->writeElement($cs.'last-name', $this->lastName); } $writer->endElement(); // organizer if ($this->commonName) { - $writer->writeElement($cs . 'organizer-cn', $this->commonName); + $writer->writeElement($cs.'organizer-cn', $this->commonName); } if ($this->firstName) { - $writer->writeElement($cs . 'organizer-first', $this->firstName); + $writer->writeElement($cs.'organizer-first', $this->firstName); } if ($this->lastName) { - $writer->writeElement($cs . 'organizer-last', $this->lastName); + $writer->writeElement($cs.'organizer-last', $this->lastName); } if ($this->supportedComponents) { - $writer->writeElement('{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set', $this->supportedComponents); + $writer->writeElement('{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set', $this->supportedComponents); } $writer->endElement(); // invite-notification - } /** - * Returns a unique id for this notification + * 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() { - + public function getId() + { return $this->id; - } /** @@ -293,10 +287,8 @@ class Invite implements NotificationInterface { * * @return string */ - function getETag() { - + public function getETag() + { return $this->etag; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php index f4b10a396bc..abcbde1511c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/InviteReply.php @@ -1,5 +1,7 @@ $value) { if (!property_exists($this, $key)) { - throw new \InvalidArgumentException('Unknown option: ' . $key); + throw new \InvalidArgumentException('Unknown option: '.$key); } $this->$key = $value; } - } /** @@ -133,12 +134,10 @@ class InviteReply implements NotificationInterface { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - - $writer->writeElement('{' . CalDAV\Plugin::NS_CALENDARSERVER . '}invite-reply'); - + public function xmlSerialize(Writer $writer) + { + $writer->writeElement('{'.CalDAV\Plugin::NS_CALENDARSERVER.'}invite-reply'); } /** @@ -146,55 +145,50 @@ class InviteReply implements NotificationInterface { * response body. * * @param Writer $writer - * @return void */ - function xmlSerializeFull(Writer $writer) { + public function xmlSerializeFull(Writer $writer) + { + $cs = '{'.CalDAV\Plugin::NS_CALENDARSERVER.'}'; - $cs = '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}'; + $this->dtStamp->setTimezone(new \DateTimeZone('GMT')); + $writer->writeElement($cs.'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z')); - $this->dtStamp->setTimezone(new \DateTimezone('GMT')); - $writer->writeElement($cs . 'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z')); + $writer->startElement($cs.'invite-reply'); - $writer->startElement($cs . 'invite-reply'); - - $writer->writeElement($cs . 'uid', $this->id); - $writer->writeElement($cs . 'in-reply-to', $this->inReplyTo); + $writer->writeElement($cs.'uid', $this->id); + $writer->writeElement($cs.'in-reply-to', $this->inReplyTo); $writer->writeElement('{DAV:}href', $this->href); switch ($this->type) { - - case DAV\Sharing\Plugin::INVITE_ACCEPTED : - $writer->writeElement($cs . 'invite-accepted'); + case DAV\Sharing\Plugin::INVITE_ACCEPTED: + $writer->writeElement($cs.'invite-accepted'); break; - case DAV\Sharing\Plugin::INVITE_DECLINED : - $writer->writeElement($cs . 'invite-declined'); + case DAV\Sharing\Plugin::INVITE_DECLINED: + $writer->writeElement($cs.'invite-declined'); break; - } - $writer->writeElement($cs . 'hosturl', [ - '{DAV:}href' => $writer->contextUri . $this->hostUrl + $writer->writeElement($cs.'hosturl', [ + '{DAV:}href' => $writer->contextUri.$this->hostUrl, ]); if ($this->summary) { - $writer->writeElement($cs . 'summary', $this->summary); + $writer->writeElement($cs.'summary', $this->summary); } $writer->endElement(); // invite-reply - } /** - * Returns a unique id for this notification + * 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() { - + public function getId() + { return $this->id; - } /** @@ -204,10 +198,8 @@ class InviteReply implements NotificationInterface { * * @return string */ - function getETag() { - + public function getETag() + { return $this->etag; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php index b98f9c88889..be749053315 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/NotificationInterface.php @@ -1,5 +1,7 @@ id = $id; $this->type = $type; $this->description = $description; $this->href = $href; $this->etag = $etag; - } /** @@ -91,27 +92,25 @@ class SystemStatus implements NotificationInterface { * responsible for closing them. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { switch ($this->type) { - case self::TYPE_LOW : + case self::TYPE_LOW: $type = 'low'; break; - case self::TYPE_MEDIUM : + case self::TYPE_MEDIUM: $type = 'medium'; break; - default : - case self::TYPE_HIGH : + default: + case self::TYPE_HIGH: $type = 'high'; break; } - $writer->startElement('{' . Plugin::NS_CALENDARSERVER . '}systemstatus'); + $writer->startElement('{'.Plugin::NS_CALENDARSERVER.'}systemstatus'); $writer->writeAttribute('type', $type); $writer->endElement(); - } /** @@ -119,51 +118,47 @@ class SystemStatus implements NotificationInterface { * response body. * * @param Writer $writer - * @return void */ - function xmlSerializeFull(Writer $writer) { - - $cs = '{' . Plugin::NS_CALENDARSERVER . '}'; + public function xmlSerializeFull(Writer $writer) + { + $cs = '{'.Plugin::NS_CALENDARSERVER.'}'; switch ($this->type) { - case self::TYPE_LOW : + case self::TYPE_LOW: $type = 'low'; break; - case self::TYPE_MEDIUM : + case self::TYPE_MEDIUM: $type = 'medium'; break; - default : - case self::TYPE_HIGH : + default: + case self::TYPE_HIGH: $type = 'high'; break; } - $writer->startElement($cs . 'systemstatus'); + $writer->startElement($cs.'systemstatus'); $writer->writeAttribute('type', $type); - if ($this->description) { - $writer->writeElement($cs . 'description', $this->description); + $writer->writeElement($cs.'description', $this->description); } if ($this->href) { $writer->writeElement('{DAV:}href', $this->href); } $writer->endElement(); // systemstatus - } /** - * Returns a unique id for this notification + * 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() { - + public function getId() + { return $this->id; - } /* @@ -173,10 +168,8 @@ class SystemStatus implements NotificationInterface { * * @return string */ - function getETag() { - + public function getETag() + { return $this->etag; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php index 54e5a116a6a..224f52c969c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/AllowedSharingModes.php @@ -1,5 +1,7 @@ canBeShared = $canBeShared; $this->canBePublished = $canBePublished; - } /** @@ -69,19 +70,14 @@ class AllowedSharingModes implements XmlSerializable { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { if ($this->canBeShared) { - $writer->writeElement('{' . Plugin::NS_CALENDARSERVER . '}can-be-shared'); + $writer->writeElement('{'.Plugin::NS_CALENDARSERVER.'}can-be-shared'); } if ($this->canBePublished) { - $writer->writeElement('{' . Plugin::NS_CALENDARSERVER . '}can-be-published'); + $writer->writeElement('{'.Plugin::NS_CALENDARSERVER.'}can-be-published'); } - } - - - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php index fc6f1d505fa..6b28d5df205 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/EmailAddressSet.php @@ -1,12 +1,14 @@ emails = $emails; - } /** - * Returns the email addresses + * Returns the email addresses. * * @return array */ - function getValue() { - + public function getValue() + { return $this->emails; - } /** @@ -65,16 +65,11 @@ class EmailAddressSet implements XmlSerializable { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->emails as $email) { - $writer->writeElement('{http://calendarserver.org/ns/}email-address', $email); - } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/Invite.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/Invite.php index 4f33c464cc3..db456617cb7 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/Invite.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/Invite.php @@ -1,5 +1,7 @@ sharees = $sharees; - } /** @@ -45,10 +47,9 @@ class Invite implements XmlSerializable { * * @return array */ - function getValue() { - + public function getValue() + { return $this->sharees; - } /** @@ -68,61 +69,54 @@ class Invite implements XmlSerializable { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - - $cs = '{' . Plugin::NS_CALENDARSERVER . '}'; + public function xmlSerialize(Writer $writer) + { + $cs = '{'.Plugin::NS_CALENDARSERVER.'}'; foreach ($this->sharees as $sharee) { - - if ($sharee->access === DAV\Sharing\Plugin::ACCESS_SHAREDOWNER) { - $writer->startElement($cs . 'organizer'); + if (DAV\Sharing\Plugin::ACCESS_SHAREDOWNER === $sharee->access) { + $writer->startElement($cs.'organizer'); } else { - $writer->startElement($cs . 'user'); + $writer->startElement($cs.'user'); switch ($sharee->inviteStatus) { - case DAV\Sharing\Plugin::INVITE_ACCEPTED : - $writer->writeElement($cs . 'invite-accepted'); + case DAV\Sharing\Plugin::INVITE_ACCEPTED: + $writer->writeElement($cs.'invite-accepted'); break; - case DAV\Sharing\Plugin::INVITE_DECLINED : - $writer->writeElement($cs . 'invite-declined'); + case DAV\Sharing\Plugin::INVITE_DECLINED: + $writer->writeElement($cs.'invite-declined'); break; - case DAV\Sharing\Plugin::INVITE_NORESPONSE : - $writer->writeElement($cs . 'invite-noresponse'); + case DAV\Sharing\Plugin::INVITE_NORESPONSE: + $writer->writeElement($cs.'invite-noresponse'); break; - case DAV\Sharing\Plugin::INVITE_INVALID : - $writer->writeElement($cs . 'invite-invalid'); + case DAV\Sharing\Plugin::INVITE_INVALID: + $writer->writeElement($cs.'invite-invalid'); break; } - $writer->startElement($cs . 'access'); + $writer->startElement($cs.'access'); switch ($sharee->access) { - case DAV\Sharing\Plugin::ACCESS_READWRITE : - $writer->writeElement($cs . 'read-write'); + case DAV\Sharing\Plugin::ACCESS_READWRITE: + $writer->writeElement($cs.'read-write'); break; - case DAV\Sharing\Plugin::ACCESS_READ : - $writer->writeElement($cs . 'read'); + case DAV\Sharing\Plugin::ACCESS_READ: + $writer->writeElement($cs.'read'); break; - } $writer->endElement(); // access - } $href = new DAV\Xml\Property\Href($sharee->href); $href->xmlSerialize($writer); if (isset($sharee->properties['{DAV:}displayname'])) { - $writer->writeElement($cs . 'common-name', $sharee->properties['{DAV:}displayname']); + $writer->writeElement($cs.'common-name', $sharee->properties['{DAV:}displayname']); } if ($sharee->comment) { - $writer->writeElement($cs . 'summary', $sharee->comment); + $writer->writeElement($cs.'summary', $sharee->comment); } $writer->endElement(); // organizer or user - } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php index 10c20be55c8..7809071699e 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/ScheduleCalendarTransp.php @@ -1,5 +1,7 @@ value = $value; - } /** - * Returns the current value + * Returns the current value. * * @return string */ - function getValue() { - + public function getValue() + { return $this->value; - } /** @@ -78,19 +78,17 @@ class ScheduleCalendarTransp implements Element { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { switch ($this->value) { - case self::TRANSPARENT : - $writer->writeElement('{' . Plugin::NS_CALDAV . '}transparent'); + case self::TRANSPARENT: + $writer->writeElement('{'.Plugin::NS_CALDAV.'}transparent'); break; - case self::OPAQUE : - $writer->writeElement('{' . Plugin::NS_CALDAV . '}opaque'); + case self::OPAQUE: + $writer->writeElement('{'.Plugin::NS_CALDAV.'}opaque'); break; } - } /** @@ -112,10 +110,11 @@ class ScheduleCalendarTransp implements Element { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $elems = Deserializer\enum($reader, Plugin::NS_CALDAV); if (in_array('transparent', $elems)) { @@ -123,8 +122,7 @@ class ScheduleCalendarTransp implements Element { } else { $value = self::OPAQUE; } + return new self($value); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php index 7fc25c5f0c2..56fa61b137f 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarComponentSet.php @@ -1,5 +1,7 @@ components = $components; - } /** - * Returns the list of supported components + * Returns the list of supported components. * * @return array */ - function getValue() { - + public function getValue() + { return $this->components; - } /** @@ -71,18 +71,14 @@ class SupportedCalendarComponentSet implements Element { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->components as $component) { - - $writer->startElement('{' . Plugin::NS_CALDAV . '}comp'); + $writer->startElement('{'.Plugin::NS_CALDAV.'}comp'); $writer->writeAttributes(['name' => $component]); $writer->endElement(); - } - } /** @@ -104,16 +100,17 @@ class SupportedCalendarComponentSet implements Element { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $elems = $reader->parseInnerTree(); $components = []; - foreach ((array)$elems as $elem) { - if ($elem['name'] === '{' . Plugin::NS_CALDAV . '}comp') { + foreach ((array) $elems as $elem) { + if ($elem['name'] === '{'.Plugin::NS_CALDAV.'}comp') { $components[] = $elem['attributes']['name']; } } @@ -123,7 +120,5 @@ class SupportedCalendarComponentSet implements Element { } return new self($components); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php index d123ba4c0d2..1c9c4779fc5 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCalendarData.php @@ -1,5 +1,7 @@ startElement('{' . Plugin::NS_CALDAV . '}calendar-data'); + public function xmlSerialize(Writer $writer) + { + $writer->startElement('{'.Plugin::NS_CALDAV.'}calendar-data'); $writer->writeAttributes([ 'content-type' => 'text/calendar', - 'version' => '2.0', + 'version' => '2.0', ]); $writer->endElement(); // calendar-data - $writer->startElement('{' . Plugin::NS_CALDAV . '}calendar-data'); + $writer->startElement('{'.Plugin::NS_CALDAV.'}calendar-data'); $writer->writeAttributes([ 'content-type' => 'application/calendar+json', ]); $writer->endElement(); // calendar-data - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php index af10860d077..b88cd0d924d 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Property/SupportedCollationSet.php @@ -1,5 +1,7 @@ writeElement('{' . Plugin::NS_CALDAV . '}supported-collation', $collation); + $writer->writeElement('{'.Plugin::NS_CALDAV.'}supported-collation', $collation); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php index 6d3c5d5089c..8231de6bda2 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php @@ -1,5 +1,7 @@ parseInnerTree([ '{urn:ietf:params:xml:ns:caldav}calendar-data' => 'Sabre\\CalDAV\\Xml\\Filter\\CalendarData', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', ]); $newProps = [ - 'hrefs' => [], + 'hrefs' => [], 'properties' => [], ]; foreach ($elems as $elem) { - switch ($elem['name']) { - - case '{DAV:}prop' : + case '{DAV:}prop': $newProps['properties'] = array_keys($elem['value']); - if (isset($elem['value']['{' . Plugin::NS_CALDAV . '}calendar-data'])) { - $newProps += $elem['value']['{' . Plugin::NS_CALDAV . '}calendar-data']; + if (isset($elem['value']['{'.Plugin::NS_CALDAV.'}calendar-data'])) { + $newProps += $elem['value']['{'.Plugin::NS_CALDAV.'}calendar-data']; } break; - case '{DAV:}href' : + case '{DAV:}href': $newProps['hrefs'][] = Uri\resolve($reader->contextUri, $elem['value']); break; - } - } $obj = new self(); @@ -118,7 +117,5 @@ class CalendarMultiGetReport implements XmlDeserializable { } return $obj; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php index e0b1c795032..e85eccd2dc4 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php @@ -1,5 +1,7 @@ parseInnerTree([ - '{urn:ietf:params:xml:ns:caldav}comp-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\CompFilter', - '{urn:ietf:params:xml:ns:caldav}prop-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\PropFilter', - '{urn:ietf:params:xml:ns:caldav}param-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\ParamFilter', + '{urn:ietf:params:xml:ns:caldav}comp-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\CompFilter', + '{urn:ietf:params:xml:ns:caldav}prop-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\PropFilter', + '{urn:ietf:params:xml:ns:caldav}param-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\ParamFilter', '{urn:ietf:params:xml:ns:caldav}calendar-data' => 'Sabre\\CalDAV\\Xml\\Filter\\CalendarData', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', ]); $newProps = [ - 'filters' => null, + 'filters' => null, 'properties' => [], ]; - if (!is_array($elems)) $elems = []; + if (!is_array($elems)) { + $elems = []; + } foreach ($elems as $elem) { - switch ($elem['name']) { - - case '{DAV:}prop' : + case '{DAV:}prop': $newProps['properties'] = array_keys($elem['value']); - if (isset($elem['value']['{' . Plugin::NS_CALDAV . '}calendar-data'])) { - $newProps += $elem['value']['{' . Plugin::NS_CALDAV . '}calendar-data']; + if (isset($elem['value']['{'.Plugin::NS_CALDAV.'}calendar-data'])) { + $newProps += $elem['value']['{'.Plugin::NS_CALDAV.'}calendar-data']; } break; - case '{' . Plugin::NS_CALDAV . '}filter' : + case '{'.Plugin::NS_CALDAV.'}filter': foreach ($elem['value'] as $subElem) { - if ($subElem['name'] === '{' . Plugin::NS_CALDAV . '}comp-filter') { + if ($subElem['name'] === '{'.Plugin::NS_CALDAV.'}comp-filter') { if (!is_null($newProps['filters'])) { throw new BadRequest('Only one top-level comp-filter may be defined'); } @@ -119,21 +122,18 @@ class CalendarQueryReport implements XmlDeserializable { } } break; - } - } if (is_null($newProps['filters'])) { - throw new BadRequest('The {' . Plugin::NS_CALDAV . '}filter element is required for this request'); + throw new BadRequest('The {'.Plugin::NS_CALDAV.'}filter element is required for this request'); } $obj = new self(); foreach ($newProps as $key => $value) { $obj->$key = $value; } + return $obj; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php index 0f6c1e0741d..a4d98a8d432 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/FreeBusyQueryReport.php @@ -1,5 +1,7 @@ parseInnerTree([]) as $elem) { - - if ($elem['name'] !== $timeRange) continue; + foreach ((array) $reader->parseInnerTree([]) as $elem) { + if ($elem['name'] !== $timeRange) { + continue; + } $start = empty($elem['attributes']['start']) ?: $elem['attributes']['start']; $end = empty($elem['attributes']['end']) ?: $elem['attributes']['end']; - } if (!$start && !$end) { throw new BadRequest('The freebusy report must have a time-range element'); @@ -85,7 +88,5 @@ class FreeBusyQueryReport implements XmlDeserializable { $result->end = $end; return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php index db32cc6a59e..710095bb296 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/InviteReply.php @@ -1,5 +1,7 @@ href = $href; $this->calendarUri = $calendarUri; $this->inReplyTo = $inReplyTo; $this->summary = $summary; $this->status = $status; - } /** @@ -98,10 +99,11 @@ class InviteReply implements XmlDeserializable { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $elems = KeyValue::xmlDeserialize($reader); $href = null; @@ -111,40 +113,35 @@ class InviteReply implements XmlDeserializable { $status = null; foreach ($elems as $name => $value) { - switch ($name) { - - case '{' . Plugin::NS_CALENDARSERVER . '}hosturl' : + case '{'.Plugin::NS_CALENDARSERVER.'}hosturl': foreach ($value as $bla) { - if ($bla['name'] === '{DAV:}href') { + if ('{DAV:}href' === $bla['name']) { $calendarUri = $bla['value']; } } break; - case '{' . Plugin::NS_CALENDARSERVER . '}invite-accepted' : + case '{'.Plugin::NS_CALENDARSERVER.'}invite-accepted': $status = DAV\Sharing\Plugin::INVITE_ACCEPTED; break; - case '{' . Plugin::NS_CALENDARSERVER . '}invite-declined' : + case '{'.Plugin::NS_CALENDARSERVER.'}invite-declined': $status = DAV\Sharing\Plugin::INVITE_DECLINED; break; - case '{' . Plugin::NS_CALENDARSERVER . '}in-reply-to' : + case '{'.Plugin::NS_CALENDARSERVER.'}in-reply-to': $inReplyTo = $value; break; - case '{' . Plugin::NS_CALENDARSERVER . '}summary' : + case '{'.Plugin::NS_CALENDARSERVER.'}summary': $summary = $value; break; - case '{DAV:}href' : + case '{DAV:}href': $href = $value; break; } - } if (is_null($calendarUri)) { throw new BadRequest('The {http://calendarserver.org/ns/}hosturl/{DAV:}href element must exist'); } return new self($href, $calendarUri, $inReplyTo, $summary, $status); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php index ce7fafde910..7cad98da535 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php @@ -1,5 +1,7 @@ properties; - } /** @@ -55,10 +56,11 @@ class MkCalendar implements XmlDeserializable { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $self = new self(); $elementMap = $reader->elementMap; @@ -67,13 +69,11 @@ class MkCalendar implements XmlDeserializable { $elems = $reader->parseInnerTree($elementMap); foreach ($elems as $elem) { - if ($elem['name'] === '{DAV:}set') { + if ('{DAV:}set' === $elem['name']) { $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']); } } return $self; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/Share.php b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/Share.php index e0bd8e0af31..60bd579d5e6 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/Share.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Request/Share.php @@ -1,5 +1,7 @@ sharees = $sharees; - } /** @@ -57,55 +58,52 @@ class Share implements XmlDeserializable { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $elems = $reader->parseGetElements([ - '{' . Plugin::NS_CALENDARSERVER . '}set' => 'Sabre\\Xml\\Element\\KeyValue', - '{' . Plugin::NS_CALENDARSERVER . '}remove' => 'Sabre\\Xml\\Element\\KeyValue', + '{'.Plugin::NS_CALENDARSERVER.'}set' => 'Sabre\\Xml\\Element\\KeyValue', + '{'.Plugin::NS_CALENDARSERVER.'}remove' => 'Sabre\\Xml\\Element\\KeyValue', ]); $sharees = []; foreach ($elems as $elem) { switch ($elem['name']) { - - case '{' . Plugin::NS_CALENDARSERVER . '}set' : + case '{'.Plugin::NS_CALENDARSERVER.'}set': $sharee = $elem['value']; - $sumElem = '{' . Plugin::NS_CALENDARSERVER . '}summary'; - $commonName = '{' . Plugin::NS_CALENDARSERVER . '}common-name'; + $sumElem = '{'.Plugin::NS_CALENDARSERVER.'}summary'; + $commonName = '{'.Plugin::NS_CALENDARSERVER.'}common-name'; $properties = []; if (isset($sharee[$commonName])) { $properties['{DAV:}displayname'] = $sharee[$commonName]; } - $access = array_key_exists('{' . Plugin::NS_CALENDARSERVER . '}read-write', $sharee) + $access = array_key_exists('{'.Plugin::NS_CALENDARSERVER.'}read-write', $sharee) ? \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE : \Sabre\DAV\Sharing\Plugin::ACCESS_READ; $sharees[] = new Sharee([ - 'href' => $sharee['{DAV:}href'], + 'href' => $sharee['{DAV:}href'], 'properties' => $properties, - 'access' => $access, - 'comment' => isset($sharee[$sumElem]) ? $sharee[$sumElem] : null + 'access' => $access, + 'comment' => isset($sharee[$sumElem]) ? $sharee[$sumElem] : null, ]); break; - case '{' . Plugin::NS_CALENDARSERVER . '}remove' : + case '{'.Plugin::NS_CALENDARSERVER.'}remove': $sharees[] = new Sharee([ - 'href' => $elem['value']['{DAV:}href'], - 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS + 'href' => $elem['value']['{DAV:}href'], + 'access' => \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS, ]); break; - } } return new self($sharees); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBook.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBook.php index c9d28a091c8..434801554c8 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBook.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBook.php @@ -1,12 +1,14 @@ carddavBackend = $carddavBackend; $this->addressBookInfo = $addressBookInfo; - } /** - * Returns the name of the addressbook + * Returns the name of the addressbook. * * @return string */ - function getName() { - + public function getName() + { return $this->addressBookInfo['uri']; - } /** - * Returns a card + * Returns a card. * * @param string $name + * * @return Card */ - function getChild($name) { - + public 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); + if (!$obj) { + throw new DAV\Exception\NotFound('Card not found'); + } + return new Card($this->carddavBackend, $this->addressBookInfo, $obj); } /** - * Returns the full list of cards + * Returns the full list of cards. * * @return array */ - function getChildren() { - + public 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; + return $children; } /** @@ -94,47 +97,47 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie * If any children are not found, you do not have to return them. * * @param string[] $paths + * * @return array */ - function getMultipleChildren(array $paths) { - + public 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; + return $children; } /** - * Creates a new directory + * Creates a new directory. * * We actually block this, as subdirectories are not allowed in addressbooks. * * @param string $name - * @return void */ - function createDirectory($name) { - + public function createDirectory($name) + { throw new DAV\Exception\MethodNotAllowed('Creating collections in addressbooks is not allowed'); - } /** - * Creates a new file + * 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 string $name * @param resource $vcardData + * * @return string|null */ - function createFile($name, $vcardData = null) { - + public function createFile($name, $vcardData = null) + { if (is_resource($vcardData)) { $vcardData = stream_get_contents($vcardData); } @@ -142,41 +145,32 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie $vcardData = DAV\StringUtil::ensureUTF8($vcardData); return $this->carddavBackend->createCard($this->addressBookInfo['id'], $name, $vcardData); - } /** * Deletes the entire addressbook. - * - * @return void */ - function delete() { - + public function delete() + { $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']); - } /** - * Renames the addressbook + * Renames the addressbook. * * @param string $newName - * @return void */ - function setName($newName) { - + public 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() { - + public function getLastModified() + { return null; - } /** @@ -189,12 +183,10 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie * Read the PropPatch documentation for more information. * * @param DAV\PropPatch $propPatch - * @return void */ - function propPatch(DAV\PropPatch $propPatch) { - + public function propPatch(DAV\PropPatch $propPatch) + { return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $propPatch); - } /** @@ -206,39 +198,33 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie * If the array is empty, it means 'all properties' were requested. * * @param array $properties + * * @return array */ - function getProperties($properties) { - + public function getProperties($properties) + { $response = []; foreach ($properties as $propertyName) { - if (isset($this->addressBookInfo[$propertyName])) { - $response[$propertyName] = $this->addressBookInfo[$propertyName]; - } - } return $response; - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->addressBookInfo['principaluri']; - } - /** * This method returns the ACL's for card nodes in this address book. * The result of this method automatically gets passed to the @@ -246,8 +232,8 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie * * @return array */ - function getChildACL() { - + public function getChildACL() + { return [ [ 'privilege' => '{DAV:}all', @@ -255,10 +241,8 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie 'protected' => true, ], ]; - } - /** * This method returns the current sync-token for this collection. * This can be any string. @@ -268,8 +252,8 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie * * @return string|null */ - function getSyncToken() { - + public function getSyncToken() + { if ( $this->carddavBackend instanceof Backend\SyncSupport && isset($this->addressBookInfo['{DAV:}sync-token']) @@ -282,7 +266,6 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie ) { return $this->addressBookInfo['{http://sabredav.org/ns}sync-token']; } - } /** @@ -336,12 +319,13 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie * The limit is 'suggestive'. You are free to ignore it. * * @param string $syncToken - * @param int $syncLevel - * @param int $limit + * @param int $syncLevel + * @param int $limit + * * @return array */ - function getChanges($syncToken, $syncLevel, $limit = null) { - + public function getChanges($syncToken, $syncLevel, $limit = null) + { if (!$this->carddavBackend instanceof Backend\SyncSupport) { return null; } @@ -352,6 +336,5 @@ class AddressBook extends DAV\Collection implements IAddressBook, DAV\IPropertie $syncLevel, $limit ); - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookHome.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookHome.php index d770c0ffe0f..fb03000aa54 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookHome.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookHome.php @@ -1,5 +1,7 @@ carddavBackend = $carddavBackend; $this->principalUri = $principalUri; - } /** - * Returns the name of this object + * Returns the name of this object. * * @return string */ - function getName() { - + public function getName() + { list(, $name) = Uri\split($this->principalUri); - return $name; + return $name; } /** - * Updates the name of this object + * Updates the name of this object. * * @param string $name - * @return void */ - function setName($name) { - + public function setName($name) + { throw new DAV\Exception\MethodNotAllowed(); - } /** - * Deletes this object - * - * @return void + * Deletes this object. */ - function delete() { - + public function delete() + { throw new DAV\Exception\MethodNotAllowed(); - } /** - * Returns the last modification date + * Returns the last modification date. * * @return int */ - function getLastModified() { - + public function getLastModified() + { return null; - } /** @@ -98,14 +93,12 @@ class AddressBookHome extends DAV\Collection implements DAV\IExtendedCollection, * * This is currently not allowed * - * @param string $filename + * @param string $filename * @param resource $data - * @return void */ - function createFile($filename, $data = null) { - + public function createFile($filename, $data = null) + { throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); - } /** @@ -114,78 +107,74 @@ class AddressBookHome extends DAV\Collection implements DAV\IExtendedCollection, * This is currently not allowed. * * @param string $filename - * @return void */ - function createDirectory($filename) { - + public function createDirectory($filename) + { throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); - } /** - * Returns a single addressbook, by name + * Returns a single addressbook, by name. * * @param string $name + * * @todo needs optimizing + * * @return AddressBook */ - function getChild($name) { - + public function getChild($name) + { foreach ($this->getChildren() as $child) { - if ($name == $child->getName()) + if ($name == $child->getName()) { return $child; - + } } - throw new DAV\Exception\NotFound('Addressbook with name \'' . $name . '\' could not be found'); - + throw new DAV\Exception\NotFound('Addressbook with name \''.$name.'\' could not be found'); } /** - * Returns a list of addressbooks + * Returns a list of addressbooks. * * @return array */ - function getChildren() { - + public function getChildren() + { $addressbooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri); $objs = []; foreach ($addressbooks as $addressbook) { $objs[] = new AddressBook($this->carddavBackend, $addressbook); } - return $objs; + return $objs; } /** * Creates a new address book. * * @param string $name - * @param MkCol $mkCol + * @param MkCol $mkCol + * * @throws DAV\Exception\InvalidResourceType - * @return void */ - function createExtendedCollection($name, MkCol $mkCol) { - - if (!$mkCol->hasResourceType('{' . Plugin::NS_CARDDAV . '}addressbook')) { + public function createExtendedCollection($name, MkCol $mkCol) + { + if (!$mkCol->hasResourceType('{'.Plugin::NS_CARDDAV.'}addressbook')) { throw new DAV\Exception\InvalidResourceType('Unknown resourceType for this collection'); } $properties = $mkCol->getRemainingValues(); $mkCol->setRemainingResultCode(201); $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties); - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->principalUri; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookRoot.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookRoot.php index a9f1183da49..219f98906cc 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookRoot.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookRoot.php @@ -1,11 +1,13 @@ carddavBackend = $carddavBackend; parent::__construct($principalBackend, $principalPrefix); - } /** - * Returns the name of the node + * Returns the name of the node. * * @return string */ - function getName() { - + public function getName() + { return Plugin::ADDRESSBOOK_ROOT; - } /** @@ -69,12 +69,11 @@ class AddressBookRoot extends DAVACL\AbstractPrincipalCollection { * supplied by the authentication backend. * * @param array $principal + * * @return \Sabre\DAV\INode */ - function getChildForPrincipal(array $principal) { - + public function getChildForPrincipal(array $principal) + { return new AddressBookHome($this->carddavBackend, $principal['uri']); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php index 03d2346da08..6b041ade4e0 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php @@ -1,9 +1,11 @@ getCard($addressBookId, $uri); }, $uris); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/BackendInterface.php index 18c0c0a9982..804f09a362b 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/BackendInterface.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/BackendInterface.php @@ -1,9 +1,11 @@ pdo = $pdo; - } /** * 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 = ?'); + public 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', + '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. * @@ -95,32 +93,29 @@ class PDO extends AbstractBackend implements SyncSupport { * * Read the PropPatch documentation for more info and examples. * - * @param string $addressBookId + * @param string $addressBookId * @param \Sabre\DAV\PropPatch $propPatch - * @return void */ - function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) { - + public function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) + { $supportedProperties = [ '{DAV:}displayname', - '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description', + '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description', ]; - $propPatch->handle($supportedProperties, function($mutations) use ($addressBookId) { - + $propPatch->handle($supportedProperties, function ($mutations) use ($addressBookId) { $updates = []; foreach ($mutations as $property => $newValue) { - switch ($property) { - case '{DAV:}displayname' : + case '{DAV:}displayname': $updates['displayname'] = $newValue; break; - case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : + case '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description': $updates['description'] = $newValue; break; } } - $query = 'UPDATE ' . $this->addressBooksTableName . ' SET '; + $query = 'UPDATE '.$this->addressBooksTableName.' SET '; $first = true; foreach ($updates as $key => $value) { if ($first) { @@ -128,7 +123,7 @@ class PDO extends AbstractBackend implements SyncSupport { } else { $query .= ', '; } - $query .= ' ' . $key . ' = :' . $key . ' '; + $query .= ' '.$key.' = :'.$key.' '; } $query .= ' WHERE id = :addressbookid'; @@ -137,72 +132,67 @@ class PDO extends AbstractBackend implements SyncSupport { $stmt->execute($updates); - $this->addChange($addressBookId, "", 2); + $this->addChange($addressBookId, '', 2); return true; - }); - } /** - * Creates a new address book + * Creates a new address book. * * @param string $principalUri - * @param string $url Just the 'basename' of the url. - * @param array $properties + * @param string $url just the 'basename' of the url + * @param array $properties + * * @return int Last insert id */ - function createAddressBook($principalUri, $url, array $properties) { - + public function createAddressBook($principalUri, $url, array $properties) + { $values = [ - 'displayname' => null, - 'description' => null, + 'displayname' => null, + 'description' => null, 'principaluri' => $principalUri, - 'uri' => $url, + 'uri' => $url, ]; foreach ($properties as $property => $newValue) { - switch ($property) { - case '{DAV:}displayname' : + case '{DAV:}displayname': $values['displayname'] = $newValue; break; - case '{' . CardDAV\Plugin::NS_CARDDAV . '}addressbook-description' : + case '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description': $values['description'] = $newValue; break; - default : - throw new DAV\Exception\BadRequest('Unknown property: ' . $property); + 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)'; + $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( - $this->addressBooksTableName . '_id_seq' - ); + return $this->pdo->lastInsertId( + $this->addressBooksTableName.'_id_seq' + ); } /** - * Deletes an entire addressbook and all its contents + * 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 = ?'); + public 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 = $this->pdo->prepare('DELETE FROM '.$this->addressBooksTableName.' WHERE id = ?'); $stmt->execute([$addressBookId]); - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->addressBookChangesTableName . ' WHERE addressbookid = ?'); + $stmt = $this->pdo->prepare('DELETE FROM '.$this->addressBookChangesTableName.' WHERE addressbookid = ?'); $stmt->execute([$addressBookId]); - } /** @@ -222,21 +212,22 @@ class PDO extends AbstractBackend implements SyncSupport { * 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 = ?'); + public 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'] . '"'; - $row['lastmodified'] = (int)$row['lastmodified']; + $row['etag'] = '"'.$row['etag'].'"'; + $row['lastmodified'] = (int) $row['lastmodified']; $result[] = $row; } - return $result; + return $result; } /** @@ -247,23 +238,26 @@ class PDO extends AbstractBackend implements SyncSupport { * * If the card does not exist, you must return false. * - * @param mixed $addressBookId + * @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'); + public 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; + if (!$result) { + return false; + } + + $result['etag'] = '"'.$result['etag'].'"'; + $result['lastmodified'] = (int) $result['lastmodified']; - $result['etag'] = '"' . $result['etag'] . '"'; - $result['lastmodified'] = (int)$result['lastmodified']; return $result; - } /** @@ -276,11 +270,12 @@ class PDO extends AbstractBackend implements SyncSupport { * * @param mixed $addressBookId * @param array $uris + * * @return array */ - function getMultipleCards($addressBookId, array $uris) { - - $query = 'SELECT id, uri, lastmodified, etag, size, carddata FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri IN ('; + public function getMultipleCards($addressBookId, array $uris) + { + $query = 'SELECT id, uri, lastmodified, etag, size, carddata FROM '.$this->cardsTableName.' WHERE addressbookid = ? AND uri IN ('; // Inserting a whole bunch of question marks $query .= implode(',', array_fill(0, count($uris), '?')); $query .= ')'; @@ -289,12 +284,12 @@ class PDO extends AbstractBackend implements SyncSupport { $stmt->execute(array_merge([$addressBookId], $uris)); $result = []; while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $row['etag'] = '"' . $row['etag'] . '"'; - $row['lastmodified'] = (int)$row['lastmodified']; + $row['etag'] = '"'.$row['etag'].'"'; + $row['lastmodified'] = (int) $row['lastmodified']; $result[] = $row; } - return $result; + return $result; } /** @@ -317,14 +312,15 @@ class PDO extends AbstractBackend implements SyncSupport { * * If you don't return an ETag, you can just return null. * - * @param mixed $addressBookId + * @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 (?, ?, ?, ?, ?, ?)'); + public function createCard($addressBookId, $cardUri, $cardData) + { + $stmt = $this->pdo->prepare('INSERT INTO '.$this->cardsTableName.' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (?, ?, ?, ?, ?, ?)'); $etag = md5($cardData); @@ -339,8 +335,7 @@ class PDO extends AbstractBackend implements SyncSupport { $this->addChange($addressBookId, $cardUri, 1); - return '"' . $etag . '"'; - + return '"'.$etag.'"'; } /** @@ -363,14 +358,15 @@ class PDO extends AbstractBackend implements SyncSupport { * * If you don't return an ETag, you can just return null. * - * @param mixed $addressBookId + * @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 =?'); + public 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([ @@ -379,31 +375,30 @@ class PDO extends AbstractBackend implements SyncSupport { strlen($cardData), $etag, $cardUri, - $addressBookId + $addressBookId, ]); $this->addChange($addressBookId, $cardUri, 2); - return '"' . $etag . '"'; - + return '"'.$etag.'"'; } /** - * Deletes a card + * Deletes a card. * - * @param mixed $addressBookId + * @param mixed $addressBookId * @param string $cardUri + * * @return bool */ - function deleteCard($addressBookId, $cardUri) { - - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->cardsTableName . ' WHERE addressbookid = ? AND uri = ?'); + public 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; - + return 1 === $stmt->rowCount(); } /** @@ -458,30 +453,34 @@ class PDO extends AbstractBackend implements SyncSupport { * * @param string $addressBookId * @param string $syncToken - * @param int $syncLevel - * @param int $limit + * @param int $syncLevel + * @param int $limit + * * @return array */ - function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) { - + public function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) + { // Current synctoken - $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->addressBooksTableName . ' WHERE id = ?'); + $stmt = $this->pdo->prepare('SELECT synctoken FROM '.$this->addressBooksTableName.' WHERE id = ?'); $stmt->execute([$addressBookId]); $currentToken = $stmt->fetchColumn(0); - if (is_null($currentToken)) return null; + if (is_null($currentToken)) { + return null; + } $result = [ 'syncToken' => $currentToken, - 'added' => [], - 'modified' => [], - 'deleted' => [], + '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; + $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); @@ -492,13 +491,10 @@ class PDO extends AbstractBackend implements SyncSupport { // 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; @@ -510,41 +506,38 @@ class PDO extends AbstractBackend implements SyncSupport { $result['deleted'][] = $uri; break; } - } } else { // No synctoken supplied, this is the initial sync. - $query = "SELECT uri FROM " . $this->cardsTableName . " WHERE addressbookid = ?"; + $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; + return $result; } /** * Adds a change record to the addressbookchanges table. * - * @param mixed $addressBookId + * @param mixed $addressBookId * @param string $objectUri - * @param int $operation 1 = add, 2 = modify, 3 = delete - * @return void + * @param int $operation 1 = add, 2 = modify, 3 = delete */ - 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 = ?'); + 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 + $addressBookId, ]); - $stmt = $this->pdo->prepare('UPDATE ' . $this->addressBooksTableName . ' SET synctoken = synctoken + 1 WHERE id = ?'); + $stmt = $this->pdo->prepare('UPDATE '.$this->addressBooksTableName.' SET synctoken = synctoken + 1 WHERE id = ?'); $stmt->execute([ - $addressBookId + $addressBookId, ]); - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/SyncSupport.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/SyncSupport.php index f80618a8e14..071361ed027 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/SyncSupport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/SyncSupport.php @@ -1,5 +1,7 @@ carddavBackend = $carddavBackend; $this->addressBookInfo = $addressBookInfo; $this->cardData = $cardData; - } /** - * Returns the uri for this object + * Returns the uri for this object. * * @return string */ - function getName() { - + public function getName() + { return $this->cardData['uri']; - } /** - * Returns the VCard-formatted object + * Returns the VCard-formatted object. * * @return string */ - function get() { - + public function get() + { // Pre-populating 'carddata' is optional. If we don't yet have it // already, we fetch it from the backend. if (!isset($this->cardData['carddata'])) { $this->cardData = $this->carddavBackend->getCard($this->addressBookInfo['id'], $this->cardData['uri']); } - return $this->cardData['carddata']; + return $this->cardData['carddata']; } /** - * Updates the VCard-formatted object + * Updates the VCard-formatted object. * * @param string $cardData + * * @return string|null */ - function put($cardData) { - - if (is_resource($cardData)) + public function put($cardData) + { + if (is_resource($cardData)) { $cardData = stream_get_contents($cardData); + } // Converting to UTF-8, if needed $cardData = DAV\StringUtil::ensureUTF8($cardData); @@ -98,92 +100,82 @@ class Card extends DAV\File implements ICard, DAVACL\IACL { $this->cardData['etag'] = $etag; return $etag; - } /** - * Deletes the card - * - * @return void + * Deletes the card. */ - function delete() { - + public function delete() + { $this->carddavBackend->deleteCard($this->addressBookInfo['id'], $this->cardData['uri']); - } /** - * Returns the mime content-type + * Returns the mime content-type. * * @return string */ - function getContentType() { - + public function getContentType() + { return 'text/vcard; charset=utf-8'; - } /** - * Returns an ETag for this object + * Returns an ETag for this object. * * @return string */ - function getETag() { - + public function getETag() + { if (isset($this->cardData['etag'])) { return $this->cardData['etag']; } else { $data = $this->get(); if (is_string($data)) { - return '"' . md5($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 + * Returns the last modification date as a unix timestamp. * * @return int */ - function getLastModified() { - + public function getLastModified() + { return isset($this->cardData['lastmodified']) ? $this->cardData['lastmodified'] : null; - } /** - * Returns the size of this object in bytes + * Returns the size of this object in bytes. * * @return int */ - function getSize() { - + public function getSize() + { if (array_key_exists('size', $this->cardData)) { return $this->cardData['size']; } else { return strlen($this->get()); } - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->addressBookInfo['principaluri']; - } - /** * Returns a list of ACE's for this node. * @@ -196,8 +188,8 @@ class Card extends DAV\File implements ICard, DAVACL\IACL { * * @return array */ - function getACL() { - + public function getACL() + { // An alternative acl may be specified through the cardData array. if (isset($this->cardData['acl'])) { return $this->cardData['acl']; @@ -210,7 +202,5 @@ class Card extends DAV\File implements ICard, DAVACL\IACL { 'protected' => true, ], ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/IAddressBook.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/IAddressBook.php index f80e0557554..3f489f4e50e 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/IAddressBook.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/IAddressBook.php @@ -1,11 +1,13 @@ on('propFind', [$this, 'propFindEarly']); - $server->on('propFind', [$this, 'propFindLate'], 150); - $server->on('report', [$this, 'report']); - $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); - $server->on('beforeWriteContent', [$this, 'beforeWriteContent']); - $server->on('beforeCreateFile', [$this, 'beforeCreateFile']); - $server->on('afterMethod:GET', [$this, 'httpAfterGet']); + $server->on('propFind', [$this, 'propFindEarly']); + $server->on('propFind', [$this, 'propFindLate'], 150); + $server->on('report', [$this, 'report']); + $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); + $server->on('beforeWriteContent', [$this, 'beforeWriteContent']); + $server->on('beforeCreateFile', [$this, 'beforeCreateFile']); + $server->on('afterMethod:GET', [$this, 'httpAfterGet']); $server->xml->namespaceMap[self::NS_CARDDAV] = 'card'; - $server->xml->elementMap['{' . self::NS_CARDDAV . '}addressbook-query'] = 'Sabre\\CardDAV\\Xml\\Request\\AddressBookQueryReport'; - $server->xml->elementMap['{' . self::NS_CARDDAV . '}addressbook-multiget'] = 'Sabre\\CardDAV\\Xml\\Request\\AddressBookMultiGetReport'; + $server->xml->elementMap['{'.self::NS_CARDDAV.'}addressbook-query'] = 'Sabre\\CardDAV\\Xml\\Request\\AddressBookQueryReport'; + $server->xml->elementMap['{'.self::NS_CARDDAV.'}addressbook-multiget'] = 'Sabre\\CardDAV\\Xml\\Request\\AddressBookMultiGetReport'; /* Mapping Interfaces to {DAV:}resourcetype values */ - $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook'; - $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{' . self::NS_CARDDAV . '}directory'; + $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->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->xml->elementMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Xml\\Property\\Href'; $this->server = $server; - } /** @@ -99,10 +100,9 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getFeatures() { - + public function getFeatures() + { return ['addressbook']; - } /** @@ -113,117 +113,111 @@ class Plugin extends DAV\ServerPlugin { * implement them * * @param string $uri + * * @return array */ - function getSupportedReportSet($uri) { - + public 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', + '{'.self::NS_CARDDAV.'}addressbook-multiget', + '{'.self::NS_CARDDAV.'}addressbook-query', ]; } - return []; + return []; } - /** - * Adds all CardDAV-specific properties + * Adds all CardDAV-specific properties. * * @param DAV\PropFind $propFind - * @param DAV\INode $node - * @return void + * @param DAV\INode $node */ - function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) { - - $ns = '{' . self::NS_CARDDAV . '}'; + public 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() { + $propFind->handle($ns.'max-resource-size', $this->maxResourceSize); + $propFind->handle($ns.'supported-address-data', function () { return new Xml\Property\SupportedAddressData(); }); - $propFind->handle($ns . 'supported-collation-set', function() { + $propFind->handle($ns.'supported-collation-set', function () { return new Xml\Property\SupportedCollationSet(); }); - } if ($node instanceof DAVACL\IPrincipal) { - $path = $propFind->getPath(); - $propFind->handle('{' . self::NS_CARDDAV . '}addressbook-home-set', function() use ($path) { - return new LocalHref($this->getAddressBookHomeForPrincipal($path) . '/'); - }); - - if ($this->directories) $propFind->handle('{' . self::NS_CARDDAV . '}directory-gateway', function() { - return new LocalHref($this->directories); + $propFind->handle('{'.self::NS_CARDDAV.'}addressbook-home-set', function () use ($path) { + return new LocalHref($this->getAddressBookHomeForPrincipal($path).'/'); }); + if ($this->directories) { + $propFind->handle('{'.self::NS_CARDDAV.'}directory-gateway', function () { + return new LocalHref($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) { + $propFind->handle('{'.self::NS_CARDDAV.'}address-data', function () use ($node) { $val = $node->get(); - if (is_resource($val)) + if (is_resource($val)) { $val = stream_get_contents($val); + } return $val; - }); - } - } /** - * This functions handles REPORT requests specific to CardDAV + * This functions handles REPORT requests specific to CardDAV. * - * @param string $reportName + * @param string $reportName * @param \DOMNode $dom - * @param mixed $path + * @param mixed $path + * * @return bool */ - function report($reportName, $dom, $path) { - + public function report($reportName, $dom, $path) + { switch ($reportName) { - case '{' . self::NS_CARDDAV . '}addressbook-multiget' : + case '{'.self::NS_CARDDAV.'}addressbook-multiget': $this->server->transactionType = 'report-addressbook-multiget'; $this->addressbookMultiGetReport($dom); + return false; - case '{' . self::NS_CARDDAV . '}addressbook-query' : + case '{'.self::NS_CARDDAV.'}addressbook-query': $this->server->transactionType = 'report-addressbook-query'; $this->addressBookQueryReport($dom); + return false; - default : + default: return; - } - - } /** - * Returns the addressbook home for a given principal + * 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; + protected function getAddressbookHomeForPrincipal($principal) + { + list(, $principalId) = Uri\split($principal); + return self::ADDRESSBOOK_ROOT.'/'.$principalId; } - /** * This function handles the addressbook-multiget REPORT. * @@ -231,14 +225,13 @@ class Plugin extends DAV\ServerPlugin { * of urls. Effectively avoiding a lot of redundant requests. * * @param Xml\Request\AddressBookMultiGetReport $report - * @return void */ - function addressbookMultiGetReport($report) { - + public function addressbookMultiGetReport($report) + { $contentType = $report->contentType; $version = $report->version; if ($version) { - $contentType .= '; version=' . $version; + $contentType .= '; version='.$version; } $vcardType = $this->negotiateVCard( @@ -251,17 +244,13 @@ class Plugin extends DAV\ServerPlugin { $report->hrefs ); foreach ($this->server->getPropertiesForMultiplePaths($paths, $report->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'], + 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(); @@ -269,8 +258,7 @@ class Plugin extends DAV\ServerPlugin { $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->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, 'minimal' === $prefer['return'])); } /** @@ -279,20 +267,19 @@ class Plugin extends DAV\ServerPlugin { * This plugin uses this method to ensure that Card nodes receive valid * vcard data. * - * @param string $path + * @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 + * @param resource $data + * @param bool $modified should be set to true, if this event handler + * changed &$data */ - function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) { - - if (!$node instanceof ICard) + public function beforeWriteContent($path, DAV\IFile $node, &$data, &$modified) + { + if (!$node instanceof ICard) { return; + } $this->validateVCard($data, $modified); - } /** @@ -301,20 +288,19 @@ class Plugin extends DAV\ServerPlugin { * This plugin uses this method to ensure that Card nodes receive valid * vcard data. * - * @param string $path - * @param resource $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 + * @param bool $modified should be set to true, if this event handler + * changed &$data */ - function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) { - - if (!$parentNode instanceof IAddressBook) + public function beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified) + { + if (!$parentNode instanceof IAddressBook) { return; + } $this->validateVCard($data, $modified); - } /** @@ -323,12 +309,11 @@ class Plugin extends DAV\ServerPlugin { * 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 + * @param bool $modified should be set to true, if this event handler + * changed &$data */ - protected function validateVCard(&$data, &$modified) { - + 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); @@ -337,10 +322,9 @@ class Plugin extends DAV\ServerPlugin { $before = $data; try { - // If the data starts with a [, we can reasonably assume we're dealing // with a jCal object. - if (substr($data, 0, 1) === '[') { + if ('[' === substr($data, 0, 1)) { $vobj = VObject\Reader::readJson($data); // Converting $data back to iCalendar, as that's what we @@ -350,21 +334,18 @@ class Plugin extends DAV\ServerPlugin { } 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()); - + throw new DAV\Exception\UnsupportedMediaType('This resource only supports valid vCard or jCard data. Parse error: '.$e->getMessage()); } - if ($vobj->name !== 'VCARD') { + if ('VCARD' !== $vobj->name) { throw new DAV\Exception\UnsupportedMediaType('This collection can only support vcard objects.'); } $options = VObject\Node::PROFILE_CARDDAV; $prefer = $this->server->getHTTPPrefer(); - if ($prefer['handling'] !== 'strict') { + if ('strict' !== $prefer['handling']) { $options |= VObject\Node::REPAIR; } @@ -376,7 +357,6 @@ class Plugin extends DAV\ServerPlugin { // $messages contains a list of problems with the vcard, along with // their severity. foreach ($messages as $message) { - if ($message['level'] > $highestLevel) { // Recording the highest reported error level. $highestLevel = $message['level']; @@ -384,30 +364,27 @@ class Plugin extends DAV\ServerPlugin { } switch ($message['level']) { - - case 1 : + case 1: // Level 1 means that there was a problem, but it was repaired. $modified = true; break; - case 2 : + case 2: // Level 2 means a warning, but not critical break; - case 3 : + case 3: // Level 3 means a critical error - throw new DAV\Exception\UnsupportedMediaType('Validation error in vCard: ' . $message['message']); - + throw new DAV\Exception\UnsupportedMediaType('Validation error in vCard: '.$message['message']); } - } if ($warningMessage) { $this->server->httpResponse->setHeader( 'X-Sabre-Ew-Gross', - 'vCard validation warning: ' . $warningMessage + 'vCard validation warning: '.$warningMessage ); // Re-serializing object. $data = $vobj->serialize(); - if (!$modified && strcmp($data, $before) !== 0) { + if (!$modified && 0 !== strcmp($data, $before)) { // This ensures that the system does not send an ETag back. $modified = true; } @@ -417,23 +394,21 @@ class Plugin extends DAV\ServerPlugin { $vobj->destroy(); } - /** - * This function handles the addressbook-query REPORT + * This function handles the addressbook-query REPORT. * * This report is used by the client to filter an addressbook based on a * complex query. * * @param Xml\Request\AddressBookQueryReport $report - * @return void */ - protected function addressbookQueryReport($report) { - + protected function addressbookQueryReport($report) + { $depth = $this->server->getHTTPDepth(0); - if ($depth == 0) { + if (0 == $depth) { $candidateNodes = [ - $this->server->tree->getNodeForPath($this->server->getRequestUri()) + $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'); @@ -444,7 +419,7 @@ class Plugin extends DAV\ServerPlugin { $contentType = $report->contentType; if ($report->version) { - $contentType .= '; version=' . $report->version; + $contentType .= '; version='.$report->version; } $vcardType = $this->negotiateVCard( @@ -453,9 +428,9 @@ class Plugin extends DAV\ServerPlugin { $validNodes = []; foreach ($candidateNodes as $node) { - - if (!$node instanceof ICard) + if (!$node instanceof ICard) { continue; + } $blob = $node->get(); if (is_resource($blob)) { @@ -472,31 +447,26 @@ class Plugin extends DAV\ServerPlugin { // We hit the maximum number of items, we can stop now. break; } - } $result = []; foreach ($validNodes as $validNode) { - - if ($depth == 0) { + if (0 == $depth) { $href = $this->server->getRequestUri(); } else { - $href = $this->server->getRequestUri() . '/' . $validNode->getName(); + $href = $this->server->getRequestUri().'/'.$validNode->getName(); } list($props) = $this->server->getPropertiesForPath($href, $report->properties, 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'], + 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, $report->addressDataProperties ); - } $result[] = $props; - } $prefer = $this->server->getHTTPPrefer(); @@ -504,26 +474,26 @@ class Plugin extends DAV\ServerPlugin { $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->server->httpResponse->setBody($this->server->generateMultiStatus($result, 'minimal' === $prefer['return'])); } /** * 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) + * @param array $filters + * @param string $test anyof or allof (which means OR or AND) + * * @return bool */ - function validateFilters($vcardData, array $filters, $test) { - - - if (!$filters) return true; + public function validateFilters($vcardData, array $filters, $test) + { + if (!$filters) { + return true; + } $vcard = VObject\Reader::read($vcardData); foreach ($filters as $filter) { - $isDefined = isset($vcard->{$filter['name']}); if ($filter['is-not-defined']) { if ($isDefined) { @@ -532,12 +502,9 @@ class Plugin extends DAV\ServerPlugin { $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 = []; @@ -546,44 +513,40 @@ class Plugin extends DAV\ServerPlugin { } if ($filter['text-matches']) { $texts = []; - foreach ($vProperties as $vProperty) + foreach ($vProperties as $vProperty) { $texts[] = $vProperty->getValue(); + } $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']); } - if (count($results) === 1) { + if (1 === count($results)) { $success = $results[0]; } else { - if ($filter['test'] === 'anyof') { + if ('anyof' === $filter['test']) { $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) { - + if ('anyof' === $test && $success) { // Destroy circular references to PHP will GC the object. $vcard->destroy(); return true; } - if ($test === 'allof' && !$success) { - + if ('allof' === $test && !$success) { // Destroy circular references to PHP will GC the object. $vcard->destroy(); return false; } - } // foreach - // Destroy circular references to PHP will GC the object. $vcard->destroy(); @@ -592,8 +555,7 @@ class Plugin extends DAV\ServerPlugin { // // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. - return $test === 'allof'; - + return 'allof' === $test; } /** @@ -602,19 +564,22 @@ class Plugin extends DAV\ServerPlugin { * @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 array $vProperties + * @param array $filters * @param string $test + * * @return bool */ - protected function validateParamFilters(array $vProperties, array $filters, $test) { - + 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 ($isDefined) { + break; + } } if ($filter['is-not-defined']) { @@ -624,35 +589,32 @@ class Plugin extends DAV\ServerPlugin { $success = true; } - // If there's no text-match, we can just check for existence + // 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 ($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) { + if ('anyof' === $test && $success) { return true; } - if ($test === 'allof' && !$success) { + if ('allof' === $test && !$success) { return false; } - } // If we got all the way here, it means we haven't been able to @@ -660,40 +622,41 @@ class Plugin extends DAV\ServerPlugin { // // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. - return $test === 'allof'; - + return 'allof' === $test; } /** * Validates if a text-filter can be applied to a specific property. * - * @param array $texts - * @param array $filters + * @param array $texts + * @param array $filters * @param string $test + * * @return bool */ - protected function validateTextMatches(array $texts, array $filters, $test) { - + 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 ($success) { + break; + } } if ($filter['negate-condition']) { $success = !$success; } - if ($success && $test === 'anyof') + if ($success && 'anyof' === $test) { return true; + } - if (!$success && $test == 'allof') + if (!$success && 'allof' == $test) { return false; - - + } } // If we got all the way here, it means we haven't been able to @@ -701,8 +664,7 @@ class Plugin extends DAV\ServerPlugin { // // This implies for 'anyof' that the test failed, and for 'allof' that // we succeeded. Sounds weird, but makes sense. - return $test === 'allof'; - + return 'allof' === $test; } /** @@ -712,24 +674,24 @@ class Plugin extends DAV\ServerPlugin { * propfind has been done. * * @param DAV\PropFind $propFind - * @param DAV\INode $node - * @return void + * @param DAV\INode $node */ - function propFindLate(DAV\PropFind $propFind, DAV\INode $node) { - + public 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) { + if (false === strpos((string) $this->server->httpRequest->getHeader('User-Agent'), 'Thunderbird')) { 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'); + if (null !== $contentType) { + list($part) = explode(';', $contentType); + if ('text/x-vcard' === $part || 'text/vcard' === $part) { + $propFind->set('{DAV:}getcontenttype', 'text/x-vcard'); + } } - } /** @@ -738,18 +700,20 @@ class Plugin extends DAV\ServerPlugin { * can use to create new addressbooks. * * @param DAV\INode $node - * @param string $output + * @param string $output + * * @return bool */ - function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof AddressBookHome) + public function htmlActionsPanel(DAV\INode $node, &$output) + { + if (!$node instanceof AddressBookHome) { return; + } $output .= '

Create new address book

- +

@@ -757,7 +721,6 @@ class Plugin extends DAV\ServerPlugin { '; return false; - } /** @@ -765,13 +728,13 @@ class Plugin extends DAV\ServerPlugin { * * This is used to transform data into jCal, if this was requested. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function httpAfterGet(RequestInterface $request, ResponseInterface $response) { - - if (strpos($response->getHeader('Content-Type'), 'text/vcard') === false) { + public function httpAfterGet(RequestInterface $request, ResponseInterface $response) + { + $contentType = $response->getHeader('Content-Type'); + if (null === $contentType || false === strpos($contentType, 'text/vcard')) { return; } @@ -783,9 +746,8 @@ class Plugin extends DAV\ServerPlugin { ); $response->setBody($newBody); - $response->setHeader('Content-Type', $mimeType . '; charset=utf-8'); + $response->setHeader('Content-Type', $mimeType.'; charset=utf-8'); $response->setHeader('Content-Length', strlen($newBody)); - } /** @@ -800,11 +762,12 @@ class Plugin extends DAV\ServerPlugin { * * @param string $input * @param string $mimeType + * * @return string */ - protected function negotiateVCard($input, &$mimeType = null) { - - $result = HTTP\Util::negotiate( + protected function negotiateVCard($input, &$mimeType = null) + { + $result = HTTP\negotiateContentType( $input, [ // Most often used mime-type. Version 3 @@ -823,41 +786,41 @@ class Plugin extends DAV\ServerPlugin { $mimeType = $result; switch ($result) { - - default : - case 'text/x-vcard' : - case 'text/vcard' : - case 'text/vcard; version=3.0' : + 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' : + case 'text/vcard; version=4.0': return 'vcard4'; - case 'application/vcard+json' : + case 'application/vcard+json': return 'jcard'; // @codeCoverageIgnoreStart } // @codeCoverageIgnoreEnd - } /** * Converts a vcard blob to a different version, or jcard. * * @param string|resource $data - * @param string $target - * @param array $propertiesFilter + * @param string $target + * @param array $propertiesFilter + * * @return string */ - protected function convertVCard($data, $target, array $propertiesFilter = null) { - + protected function convertVCard($data, $target, array $propertiesFilter = null) + { if (is_resource($data)) { $data = stream_get_contents($data); } $input = VObject\Reader::read($data); if (!empty($propertiesFilter)) { $propertiesFilter = array_merge(['UID', 'VERSION', 'FN'], $propertiesFilter); - $keys = array_unique(array_map(function($child) { + $keys = array_unique(array_map(function ($child) { return $child->name; }, $input->children())); $keys = array_diff($keys, $propertiesFilter); @@ -868,38 +831,36 @@ class Plugin extends DAV\ServerPlugin { } $output = null; try { - switch ($target) { - default : - case 'vcard3' : - if ($input->getDocumentType() === VObject\Document::VCARD30) { + default: + case 'vcard3': + if (VObject\Document::VCARD30 === $input->getDocumentType()) { // Do nothing return $data; } $output = $input->convert(VObject\Document::VCARD30); + return $output->serialize(); - case 'vcard4' : - if ($input->getDocumentType() === VObject\Document::VCARD40) { + case 'vcard4': + if (VObject\Document::VCARD40 === $input->getDocumentType()) { // Do nothing return $data; } $output = $input->convert(VObject\Document::VCARD40); + return $output->serialize(); - case 'jcard' : + case 'jcard': $output = $input->convert(VObject\Document::VCARD40); + return json_encode($output); - } - } finally { - // Destroy circular references to PHP will GC the object. $input->destroy(); if (!is_null($output)) { $output->destroy(); } } - } /** @@ -910,10 +871,9 @@ class Plugin extends DAV\ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'carddav'; - } /** @@ -927,14 +887,12 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds support for CardDAV (rfc6352)', - 'link' => 'http://sabre.io/dav/carddav/', + 'link' => 'http://sabre.io/dav/carddav/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/VCFExportPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/VCFExportPlugin.php index 2d61db6ac3d..194927c5312 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/VCFExportPlugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/VCFExportPlugin.php @@ -1,5 +1,7 @@ server = $server; $this->server->on('method:GET', [$this, 'httpGet'], 90); - $server->on('browserButtonActions', function($path, $node, &$actions) { + $server->on('browserButtonActions', function ($path, $node, &$actions) { if ($node instanceof IAddressBook) { - $actions .= ''; + $actions .= ''; } }); } @@ -48,20 +49,25 @@ class VCFExportPlugin extends DAV\ServerPlugin { /** * Intercepts GET requests on addressbook urls ending with ?export. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpGet(RequestInterface $request, ResponseInterface $response) { - + public function httpGet(RequestInterface $request, ResponseInterface $response) + { $queryParams = $request->getQueryParameters(); - if (!array_key_exists('export', $queryParams)) return; + if (!array_key_exists('export', $queryParams)) { + return; + } $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path); - if (!($node instanceof IAddressBook)) return; + if (!($node instanceof IAddressBook)) { + return; + } $this->server->transactionType = 'get-addressbook-export'; @@ -71,7 +77,7 @@ class VCFExportPlugin extends DAV\ServerPlugin { } $nodes = $this->server->getPropertiesForPath($path, [ - '{' . Plugin::NS_CARDDAV . '}address-data', + '{'.Plugin::NS_CARDDAV.'}address-data', ], 1); $format = 'text/directory'; @@ -91,9 +97,9 @@ class VCFExportPlugin extends DAV\ServerPlugin { '', $node->getName() ); - $filename .= '-' . date('Y-m-d') . $filenameExtension; + $filename .= '-'.date('Y-m-d').$filenameExtension; - $response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); + $response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"'); $response->setHeader('Content-Type', $format); $response->setStatus(200); @@ -101,25 +107,24 @@ class VCFExportPlugin extends DAV\ServerPlugin { // Returning false to break the event chain return false; - } /** - * Merges all vcard objects, and builds one big vcf export + * Merges all vcard objects, and builds one big vcf export. * * @param array $nodes + * * @return string */ - function generateVCF(array $nodes) { - - $output = ""; + public function generateVCF(array $nodes) + { + $output = ''; foreach ($nodes as $node) { - - if (!isset($node[200]['{' . Plugin::NS_CARDDAV . '}address-data'])) { + if (!isset($node[200]['{'.Plugin::NS_CARDDAV.'}address-data'])) { continue; } - $nodeData = $node[200]['{' . Plugin::NS_CARDDAV . '}address-data']; + $nodeData = $node[200]['{'.Plugin::NS_CARDDAV.'}address-data']; // Parsing this node so VObject can clean up the output. $vcard = VObject\Reader::read($nodeData); @@ -127,11 +132,9 @@ class VCFExportPlugin extends DAV\ServerPlugin { // Destroy circular references to PHP will GC the object. $vcard->destroy(); - } return $output; - } /** @@ -142,10 +145,9 @@ class VCFExportPlugin extends DAV\ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'vcf-export'; - } /** @@ -159,14 +161,12 @@ class VCFExportPlugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds the ability to export CardDAV addressbooks as a single vCard file.', - 'link' => 'http://sabre.io/dav/vcf-export-plugin/', + 'link' => 'http://sabre.io/dav/vcf-export-plugin/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php index a130cd61d2e..f1b651e7687 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php @@ -1,5 +1,7 @@ $reader->getAttribute('content-type') ?: 'text/vcard', - 'version' => $reader->getAttribute('version') ?: '3.0', + 'version' => $reader->getAttribute('version') ?: '3.0', ]; - $elems = (array)$reader->parseInnerTree(); - $result['addressDataProperties'] = array_map(function($element) { + $elems = (array) $reader->parseInnerTree(); + $elems = array_filter($elems, function ($element) { + return '{urn:ietf:params:xml:ns:carddav}prop' === $element['name'] && + isset($element['attributes']['name']); + }); + $result['addressDataProperties'] = array_map(function ($element) { return $element['attributes']['name']; }, $elems); return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php index 936e26917ce..2d39dea7f7e 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php @@ -1,5 +1,7 @@ null, + 'name' => null, 'is-not-defined' => false, - 'text-match' => null, + 'text-match' => null, ]; $att = $reader->parseAttributes(); @@ -57,33 +60,29 @@ abstract class ParamFilter implements Element { $elems = $reader->parseInnerTree(); - if (is_array($elems)) foreach ($elems as $elem) { - - switch ($elem['name']) { - - case '{' . Plugin::NS_CARDDAV . '}is-not-defined' : + if (is_array($elems)) { + foreach ($elems as $elem) { + switch ($elem['name']) { + case '{'.Plugin::NS_CARDDAV.'}is-not-defined': $result['is-not-defined'] = true; break; - case '{' . Plugin::NS_CARDDAV . '}text-match' : + case '{'.Plugin::NS_CARDDAV.'}text-match': $matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains'; if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) { - throw new BadRequest('Unknown match-type: ' . $matchType); + throw new BadRequest('Unknown match-type: '.$matchType); } $result['text-match'] = [ - 'negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', - 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap', - 'value' => $elem['value'], - 'match-type' => $matchType, + 'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'], + 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap', + 'value' => $elem['value'], + 'match-type' => $matchType, ]; break; - } - + } } return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php index d7799429db4..a22a577c9bc 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php @@ -1,5 +1,7 @@ null, - 'test' => 'anyof', + 'name' => null, + 'test' => 'anyof', 'is-not-defined' => false, - 'param-filters' => [], - 'text-matches' => [], + 'param-filters' => [], + 'text-matches' => [], ]; $att = $reader->parseAttributes(); $result['name'] = $att['name']; - if (isset($att['test']) && $att['test'] === 'allof') { + if (isset($att['test']) && 'allof' === $att['test']) { $result['test'] = 'allof'; } $elems = $reader->parseInnerTree(); - if (is_array($elems)) foreach ($elems as $elem) { - - switch ($elem['name']) { - - case '{' . Plugin::NS_CARDDAV . '}param-filter' : + if (is_array($elems)) { + foreach ($elems as $elem) { + switch ($elem['name']) { + case '{'.Plugin::NS_CARDDAV.'}param-filter': $result['param-filters'][] = $elem['value']; break; - case '{' . Plugin::NS_CARDDAV . '}is-not-defined' : + case '{'.Plugin::NS_CARDDAV.'}is-not-defined': $result['is-not-defined'] = true; break; - case '{' . Plugin::NS_CARDDAV . '}text-match' : + case '{'.Plugin::NS_CARDDAV.'}text-match': $matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains'; if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) { - throw new BadRequest('Unknown match-type: ' . $matchType); + throw new BadRequest('Unknown match-type: '.$matchType); } $result['text-matches'][] = [ - 'negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', - 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap', - 'value' => $elem['value'], - 'match-type' => $matchType, + 'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'], + 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap', + 'value' => $elem['value'], + 'match-type' => $matchType, ]; break; - } - + } } return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php index aecd8a09fc8..9d005169850 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedAddressData.php @@ -1,5 +1,7 @@ 'text/vcard', 'version' => '3.0'], @@ -45,7 +47,6 @@ class SupportedAddressData implements XmlSerializable { } $this->supportedData = $supportedData; - } /** @@ -65,19 +66,16 @@ class SupportedAddressData implements XmlSerializable { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->supportedData as $supported) { - $writer->startElement('{' . Plugin::NS_CARDDAV . '}address-data-type'); + $writer->startElement('{'.Plugin::NS_CARDDAV.'}address-data-type'); $writer->writeAttributes([ 'content-type' => $supported['contentType'], - 'version' => $supported['version'] + 'version' => $supported['version'], ]); $writer->endElement(); // address-data-type } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php index 778aa2b64e5..66e451a91c4 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Property/SupportedCollationSet.php @@ -1,12 +1,14 @@ writeElement('{urn:ietf:params:xml:ns:carddav}supported-collation', $coll); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php index 0115a010722..845796760e6 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php @@ -1,5 +1,7 @@ parseInnerTree([ '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', ]); $newProps = [ - 'hrefs' => [], - 'properties' => [] + 'hrefs' => [], + 'properties' => [], ]; foreach ($elems as $elem) { - switch ($elem['name']) { - - case '{DAV:}prop' : + case '{DAV:}prop': $newProps['properties'] = array_keys($elem['value']); - if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) { - $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data']; + if (isset($elem['value']['{'.Plugin::NS_CARDDAV.'}address-data'])) { + $newProps += $elem['value']['{'.Plugin::NS_CARDDAV.'}address-data']; } break; - case '{DAV:}href' : + case '{DAV:}href': $newProps['hrefs'][] = Uri\resolve($reader->contextUri, $elem['value']); break; - } - } $obj = new self(); foreach ($newProps as $key => $value) { $obj->$key = $value; } + return $obj; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php index 09fad008adb..0e6f26d3855 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php @@ -1,5 +1,7 @@ parseInnerTree([ - '{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter', + public static function xmlDeserialize(Reader $reader) + { + $elems = (array) $reader->parseInnerTree([ + '{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter', '{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter', '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', ]); $newProps = [ - 'filters' => null, + 'filters' => null, 'properties' => [], - 'test' => 'anyof', - 'limit' => null, + 'test' => 'anyof', + 'limit' => null, ]; - if (!is_array($elems)) $elems = []; + if (!is_array($elems)) { + $elems = []; + } foreach ($elems as $elem) { - switch ($elem['name']) { - - case '{DAV:}prop' : + case '{DAV:}prop': $newProps['properties'] = array_keys($elem['value']); - if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) { - $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data']; + if (isset($elem['value']['{'.Plugin::NS_CARDDAV.'}address-data'])) { + $newProps += $elem['value']['{'.Plugin::NS_CARDDAV.'}address-data']; } break; - case '{' . Plugin::NS_CARDDAV . '}filter' : + case '{'.Plugin::NS_CARDDAV.'}filter': if (!is_null($newProps['filters'])) { - throw new BadRequest('You can only include 1 {' . Plugin::NS_CARDDAV . '}filter element'); + throw new BadRequest('You can only include 1 {'.Plugin::NS_CARDDAV.'}filter element'); } if (isset($elem['attributes']['test'])) { $newProps['test'] = $elem['attributes']['test']; - if ($newProps['test'] !== 'allof' && $newProps['test'] !== 'anyof') { + if ('allof' !== $newProps['test'] && 'anyof' !== $newProps['test']) { throw new BadRequest('The "test" attribute must be one of "allof" or "anyof"'); } } $newProps['filters'] = []; - foreach ((array)$elem['value'] as $subElem) { - if ($subElem['name'] === '{' . Plugin::NS_CARDDAV . '}prop-filter') { + foreach ((array) $elem['value'] as $subElem) { + if ($subElem['name'] === '{'.Plugin::NS_CARDDAV.'}prop-filter') { $newProps['filters'][] = $subElem['value']; } } break; - case '{' . Plugin::NS_CARDDAV . '}limit' : + case '{'.Plugin::NS_CARDDAV.'}limit': foreach ($elem['value'] as $child) { - if ($child['name'] === '{' . Plugin::NS_CARDDAV . '}nresults') { - $newProps['limit'] = (int)$child['value']; + if ($child['name'] === '{'.Plugin::NS_CARDDAV.'}nresults') { + $newProps['limit'] = (int) $child['value']; } } break; - } - } if (is_null($newProps['filters'])) { @@ -184,7 +184,6 @@ class AddressBookQueryReport implements XmlDeserializable { */ //throw new BadRequest('The {' . Plugin::NS_CARDDAV . '}filter element is required for this request'); $newProps['filters'] = []; - } $obj = new self(); @@ -193,7 +192,5 @@ class AddressBookQueryReport implements XmlDeserializable { } return $obj; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php index 40a95f8bfb7..aa8b1f573d1 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php @@ -1,14 +1,15 @@ realm = $realm; - } /** @@ -86,12 +86,13 @@ abstract class AbstractBasic implements BackendInterface { * * principals/users/[username] * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return array */ - function check(RequestInterface $request, ResponseInterface $response) { - + public function check(RequestInterface $request, ResponseInterface $response) + { $auth = new HTTP\Auth\Basic( $this->realm, $request, @@ -103,10 +104,10 @@ abstract class AbstractBasic implements BackendInterface { return [false, "No 'Authorization: Basic' header found. Either the client didn't send one, or the server is misconfigured"]; } if (!$this->validateUserPass($userpass[0], $userpass[1])) { - return [false, "Username or password was incorrect"]; + return [false, 'Username or password was incorrect']; } - return [true, $this->principalPrefix . $userpass[0]]; + return [true, $this->principalPrefix.$userpass[0]]; } /** @@ -126,19 +127,16 @@ abstract class AbstractBasic implements BackendInterface { * append your own WWW-Authenticate header instead of overwriting the * existing one. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function challenge(RequestInterface $request, ResponseInterface $response) { - + public function challenge(RequestInterface $request, ResponseInterface $response) + { $auth = new HTTP\Auth\Basic( $this->realm, $request, $response ); $auth->requireLogin(); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php index ae7a8a12f77..a2653b2b046 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php @@ -1,14 +1,15 @@ realm = $realm; - } /** @@ -79,12 +79,13 @@ abstract class AbstractBearer implements BackendInterface { * * principals/users/[username] * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return array */ - function check(RequestInterface $request, ResponseInterface $response) { - + public function check(RequestInterface $request, ResponseInterface $response) + { $auth = new HTTP\Auth\Bearer( $this->realm, $request, @@ -97,10 +98,10 @@ abstract class AbstractBearer implements BackendInterface { } $principalUrl = $this->validateBearerToken($bearerToken); if (!$principalUrl) { - return [false, "Bearer token was incorrect"]; + return [false, 'Bearer token was incorrect']; } - return [true, $principalUrl]; + return [true, $principalUrl]; } /** @@ -120,19 +121,16 @@ abstract class AbstractBearer implements BackendInterface { * append your own WWW-Authenticate header instead of overwriting the * existing one. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function challenge(RequestInterface $request, ResponseInterface $response) { - + public function challenge(RequestInterface $request, ResponseInterface $response) + { $auth = new HTTP\Auth\Bearer( $this->realm, $request, $response ); $auth->requireLogin(); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php index 4b47f56c9ff..06c9ed3a489 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php @@ -1,5 +1,7 @@ realm = $realm; - } /** @@ -60,9 +60,10 @@ abstract class AbstractDigest implements BackendInterface { * * @param string $realm * @param string $username + * * @return string|null */ - abstract function getDigestHash($realm, $username); + abstract public function getDigestHash($realm, $username); /** * When this method is called, the backend must check if authentication was @@ -88,12 +89,13 @@ abstract class AbstractDigest implements BackendInterface { * * principals/users/[username] * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return array */ - function check(RequestInterface $request, ResponseInterface $response) { - + public function check(RequestInterface $request, ResponseInterface $response) + { $digest = new HTTP\Auth\Digest( $this->realm, $request, @@ -110,8 +112,8 @@ abstract class AbstractDigest implements BackendInterface { $hash = $this->getDigestHash($this->realm, $username); // If this was false, the user account didn't exist - if ($hash === false || is_null($hash)) { - return [false, "Username or password was incorrect"]; + if (false === $hash || is_null($hash)) { + return [false, 'Username or password was incorrect']; } if (!is_string($hash)) { throw new DAV\Exception('The returned value from getDigestHash must be a string or null'); @@ -119,11 +121,10 @@ abstract class AbstractDigest implements BackendInterface { // If this was false, the password or part of the hash was incorrect. if (!$digest->validateA1($hash)) { - return [false, "Username or password was incorrect"]; + return [false, 'Username or password was incorrect']; } - return [true, $this->principalPrefix . $username]; - + return [true, $this->principalPrefix.$username]; } /** @@ -143,12 +144,11 @@ abstract class AbstractDigest implements BackendInterface { * append your own WWW-Authenticate header instead of overwriting the * existing one. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function challenge(RequestInterface $request, ResponseInterface $response) { - + public function challenge(RequestInterface $request, ResponseInterface $response) + { $auth = new HTTP\Auth\Digest( $this->realm, $request, @@ -162,7 +162,5 @@ abstract class AbstractDigest implements BackendInterface { // Preventing the digest utility from modifying the http status code, // this should be handled by the main plugin. $response->setStatus($oldStatus); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/Apache.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/Apache.php index e203d26855d..201fe615cfa 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/Apache.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/Apache.php @@ -1,24 +1,26 @@ getRawServerValue('REMOTE_USER'); if (is_null($remoteUser)) { $remoteUser = $request->getRawServerValue('REDIRECT_REMOTE_USER'); } if (is_null($remoteUser)) { - return [false, 'No REMOTE_USER property was found in the PHP $_SERVER super-global. This likely means your server is not configured correctly']; + $remoteUser = $request->getRawServerValue('PHP_AUTH_USER'); + } + if (is_null($remoteUser)) { + return [false, 'No REMOTE_USER, REDIRECT_REMOTE_USER, or PHP_AUTH_USER property was found in the PHP $_SERVER super-global. This likely means your server is not configured correctly']; } - return [true, $this->principalPrefix . $remoteUser]; - + return [true, $this->principalPrefix.$remoteUser]; } /** @@ -85,12 +90,10 @@ class Apache implements BackendInterface { * append your own WWW-Authenticate header instead of overwriting the * existing one. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function challenge(RequestInterface $request, ResponseInterface $response) { - + public function challenge(RequestInterface $request, ResponseInterface $response) + { } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php index 0fb2210f488..8598791fb7d 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php @@ -1,5 +1,7 @@ callBack = $callBack; - } /** - * Validates a username and password + * 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) { - + protected function validateUserPass($username, $password) + { $cb = $this->callBack; + return $cb($username, $password); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/File.php index 3a687d747b2..ea2d3967997 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/File.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/File.php @@ -1,5 +1,7 @@ loadFile($filename); - + } } /** @@ -41,37 +43,32 @@ class File extends AbstractDigest { * more than 1 file is used. * * @param string $filename - * @return void */ - function loadFile($filename) { - + public function loadFile($filename) + { foreach (file($filename, FILE_IGNORE_NEW_LINES) as $line) { - - if (substr_count($line, ":") !== 2) + if (2 !== substr_count($line, ':')) { 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)) + if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) { throw new DAV\Exception('Malformed htdigest file. Invalid md5 hash'); - - $this->users[$realm . ':' . $username] = $A1; - + } + $this->users[$realm.':'.$username] = $A1; } - } /** - * Returns a users' information + * 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; - + public function getDigestHash($realm, $username) + { + return isset($this->users[$realm.':'.$username]) ? $this->users[$realm.':'.$username] : false; } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/IMAP.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/IMAP.php new file mode 100644 index 00000000000..3a1831116af --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/IMAP.php @@ -0,0 +1,82 @@ +mailbox = $mailbox; + } + + /** + * Connects to an IMAP server and tries to authenticate. + * + * @param string $username + * @param string $password + * + * @return bool + */ + protected function imapOpen($username, $password) + { + $success = false; + + try { + $imap = imap_open($this->mailbox, $username, $password, OP_HALFOPEN | OP_READONLY, 1); + if ($imap) { + $success = true; + } + } catch (\ErrorException $e) { + error_log($e->getMessage()); + } + + $errors = imap_errors(); + if ($errors) { + foreach ($errors as $error) { + error_log($error); + } + } + + if (isset($imap) && $imap) { + imap_close($imap); + } + + return $success; + } + + /** + * Validates a username and password by trying to authenticate against IMAP. + * + * @param string $username + * @param string $password + * + * @return bool + */ + protected function validateUserPass($username, $password) + { + return $this->imapOpen($username, $password); + } +} diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/PDO.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/PDO.php index c2f6de97480..87ead6fcd93 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/PDO.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Backend/PDO.php @@ -1,5 +1,7 @@ pdo = $pdo; - } /** @@ -44,14 +44,14 @@ class PDO extends AbstractDigest { * * @param string $realm * @param string $username + * * @return string|null */ - function getDigestHash($realm, $username) { - - $stmt = $this->pdo->prepare('SELECT digesta1 FROM ' . $this->tableName . ' WHERE username = ?'); + public function getDigestHash($realm, $username) + { + $stmt = $this->pdo->prepare('SELECT digesta1 FROM '.$this->tableName.' WHERE username = ?'); $stmt->execute([$username]); + return $stmt->fetchColumn() ?: null; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Plugin.php index bbb5d180d0c..9be90283fc5 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Plugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Auth/Plugin.php @@ -1,5 +1,7 @@ addBackend($authBackend); } - } /** * Adds an authentication backend to the plugin. * * @param Backend\BackendInterface $authBackend - * @return void */ - function addBackend(Backend\BackendInterface $authBackend) { - + public function addBackend(Backend\BackendInterface $authBackend) + { $this->backends[] = $authBackend; - } /** - * Initializes the plugin. This function is automatically called by the server + * Initializes the plugin. This function is automatically called by the server. * * @param Server $server - * @return void */ - function initialize(Server $server) { - - $server->on('beforeMethod', [$this, 'beforeMethod'], 10); - + public function initialize(Server $server) + { + $server->on('beforeMethod:*', [$this, 'beforeMethod'], 10); } /** @@ -96,10 +93,9 @@ class Plugin extends ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'auth'; - } /** @@ -114,23 +110,22 @@ class Plugin extends ServerPlugin { * * @return string|null */ - function getCurrentPrincipal() { - + public function getCurrentPrincipal() + { return $this->currentPrincipal; - } /** - * This method is called before any HTTP method and forces users to be authenticated + * This method is called before any HTTP method and forces users to be authenticated. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function beforeMethod(RequestInterface $request, ResponseInterface $response) { - + public function beforeMethod(RequestInterface $request, ResponseInterface $response) + { if ($this->currentPrincipal) { - // We already have authentication information. This means that the // event has already fired earlier, and is now likely fired for a // sub-request. @@ -144,7 +139,6 @@ class Plugin extends ServerPlugin { // // See issue #580 for more information about that. return; - } $authResult = $this->check($request, $response); @@ -153,11 +147,10 @@ class Plugin extends ServerPlugin { // Auth was successful $this->currentPrincipal = $authResult[1]; $this->loginFailedReasons = null; + return; } - - // If we got here, it means that no authentication backend was // successful in authenticating the user. $this->currentPrincipal = null; @@ -167,7 +160,6 @@ class Plugin extends ServerPlugin { $this->challenge($request, $response); throw new NotAuthenticated(implode(', ', $authResult[1])); } - } /** @@ -184,24 +176,24 @@ class Plugin extends ServerPlugin { * unsuccessful. For every auth backend there will be one reason, so usually * there's just one. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return array */ - function check(RequestInterface $request, ResponseInterface $response) { - + public function check(RequestInterface $request, ResponseInterface $response) + { if (!$this->backends) { throw new \Sabre\DAV\Exception('No authentication backends were configured on this server.'); } $reasons = []; foreach ($this->backends as $backend) { - $result = $backend->check( $request, $response ); - if (!is_array($result) || count($result) !== 2 || !is_bool($result[0]) || !is_string($result[1])) { + if (!is_array($result) || 2 !== count($result) || !is_bool($result[0]) || !is_string($result[1])) { throw new \Sabre\DAV\Exception('The authentication backend did not return a correct value from the check() method.'); } @@ -211,11 +203,9 @@ class Plugin extends ServerPlugin { return [true, $result[1]]; } $reasons[] = $result[1]; - } return [false, $reasons]; - } /** @@ -225,16 +215,16 @@ class Plugin extends ServerPlugin { * WWW-Authorization header, indicating to the client that it should * authenticate. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return array */ - function challenge(RequestInterface $request, ResponseInterface $response) { - + public function challenge(RequestInterface $request, ResponseInterface $response) + { foreach ($this->backends as $backend) { $backend->challenge($request, $response); } - } /** @@ -255,10 +245,9 @@ class Plugin extends ServerPlugin { * * @return string[]|null */ - function getLoginFailedReasons() { - + public function getLoginFailedReasons() + { return $this->loginFailedReasons; - } /** @@ -272,14 +261,12 @@ class Plugin extends ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Generic authentication plugin', - 'link' => 'http://sabre.io/dav/authentication/', + 'link' => 'http://sabre.io/dav/authentication/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/GuessContentType.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/GuessContentType.php index 3ba2aee25b2..7466babb307 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/GuessContentType.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/GuessContentType.php @@ -1,14 +1,16 @@ 'image/jpeg', 'gif' => 'image/gif', @@ -44,58 +45,52 @@ class GuessContentType extends DAV\ServerPlugin { // text 'txt' => 'text/plain', - ]; /** - * Initializes the plugin + * Initializes the plugin. * * @param DAV\Server $server - * @return void */ - function initialize(DAV\Server $server) { - + public 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 + * Our PROPFIND handler. * * Here we set a contenttype, if the node didn't already have one. * * @param PropFind $propFind - * @param INode $node - * @return void + * @param INode $node */ - function propFind(PropFind $propFind, INode $node) { + public function propFind(PropFind $propFind, INode $node) + { + $propFind->handle('{DAV:}getcontenttype', function () use ($propFind) { + list(, $fileName) = Uri\split($propFind->getPath()); - $propFind->handle('{DAV:}getcontenttype', function() use ($propFind) { - - list(, $fileName) = URLUtil::splitPath($propFind->getPath()); return $this->getContentType($fileName); - }); - } /** - * Simple method to return the contenttype + * Simple method to return the contenttype. * * @param string $fileName + * * @return string */ - protected function getContentType($fileName) { - + protected function getContentType($fileName) + { // Just grabbing the extension $extension = strtolower(substr($fileName, strrpos($fileName, '.') + 1)); if (isset($this->extensionMap[$extension])) { return $this->extensionMap[$extension]; } + return 'application/octet-stream'; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/HtmlOutput.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/HtmlOutput.php index f4be6b34841..59b3f560481 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/HtmlOutput.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/HtmlOutput.php @@ -1,5 +1,7 @@ baseUri = $baseUri; $this->namespaceMap = $namespaceMap; - } /** @@ -58,24 +59,24 @@ class HtmlOutputHelper { * Absolute urls are left alone. * * @param string $path + * * @return string */ - function fullUrl($path) { - + public function fullUrl($path) + { return Uri\resolve($this->baseUri, $path); - } /** * Escape string for HTML output. * - * @param string $input + * @param scalar $input + * * @return string */ - function h($input) { - - return htmlspecialchars($input, ENT_COMPAT, 'UTF-8'); - + public function h($input) + { + return htmlspecialchars((string) $input, ENT_COMPAT, 'UTF-8'); } /** @@ -86,13 +87,14 @@ class HtmlOutputHelper { * * @param string $url * @param string $label + * * @return string */ - function link($url, $label = null) { - + public function link($url, $label = null) + { $url = $this->h($this->fullUrl($url)); - return '' . ($label ? $this->h($label) : $url) . ''; + return ''.($label ? $this->h($label) : $url).''; } /** @@ -100,18 +102,18 @@ class HtmlOutputHelper { * shortened version with a prefix, if it was a known namespace. * * @param string $element + * * @return string */ - function xmlName($element) { - + public function xmlName($element) + { list($ns, $localName) = XmlService::parseClarkNotation($element); if (isset($this->namespaceMap[$ns])) { - $propName = $this->namespaceMap[$ns] . ':' . $localName; + $propName = $this->namespaceMap[$ns].':'.$localName; } else { $propName = $element; } - return "h($element) . "\">" . $this->h($propName) . ""; + return ''.$this->h($propName).''; } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php index 61327c49a0c..25e061128ec 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php @@ -1,5 +1,7 @@ server = $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 + * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpGet(RequestInterface $request, ResponseInterface $response) { - + public function httpGet(RequestInterface $request, ResponseInterface $response) + { $node = $this->server->tree->getNodeForPath($request->getPath()); - if ($node instanceof DAV\IFile) return; + if ($node instanceof DAV\IFile) { + return; + } $subRequest = clone $request; $subRequest->setMethod('PROPFIND'); $this->server->invokeMethod($subRequest, $response); + return false; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/Plugin.php index 545ad56337e..e2fab4b79f0 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/Plugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/Plugin.php @@ -1,15 +1,18 @@ enablePost = $enablePost; - } /** - * Initializes the plugin and subscribes to events + * Initializes the plugin and subscribes to events. * * @param DAV\Server $server - * @return void */ - function initialize(DAV\Server $server) { - + public function initialize(DAV\Server $server) + { $this->server = $server; $this->server->on('method:GET', [$this, 'httpGetEarly'], 90); $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']); + if ($this->enablePost) { + $this->server->on('method:POST', [$this, 'httpPOST']); + } } /** * This method intercepts GET requests that have ?sabreAction=info - * appended to the URL + * appended to the URL. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpGetEarly(RequestInterface $request, ResponseInterface $response) { - + public function httpGetEarly(RequestInterface $request, ResponseInterface $response) + { $params = $request->getQueryParameters(); - if (isset($params['sabreAction']) && $params['sabreAction'] === 'info') { + if (isset($params['sabreAction']) && 'info' === $params['sabreAction']) { return $this->httpGet($request, $response); } - } /** - * This method intercepts GET requests to collections and returns the html + * This method intercepts GET requests to collections and returns the html. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpGet(RequestInterface $request, ResponseInterface $response) { - + public function httpGet(RequestInterface $request, ResponseInterface $response) + { // We're not using straight-up $_GET, because we want everything to be // unit testable. $getVars = $request->getQueryParameters(); @@ -117,13 +121,13 @@ class Plugin extends DAV\ServerPlugin { $sabreAction = isset($getVars['sabreAction']) ? $getVars['sabreAction'] : null; switch ($sabreAction) { - - case 'asset' : + case 'asset': // Asset handling, such as images $this->serveAsset(isset($getVars['assetName']) ? $getVars['assetName'] : null); + return false; - default : - case 'info' : + default: + case 'info': try { $this->server->tree->getNodeForPath($request->getPath()); } catch (DAV\Exception\NotFound $e) { @@ -141,7 +145,7 @@ class Plugin extends DAV\ServerPlugin { return false; - case 'plugins' : + case 'plugins': $response->setStatus(200); $response->setHeader('Content-Type', 'text/html; charset=utf-8'); @@ -150,41 +154,39 @@ class Plugin extends DAV\ServerPlugin { ); return false; - } - } /** * Handles POST requests for tree operations. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpPOST(RequestInterface $request, ResponseInterface $response) { - + public 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; + if ('application/x-www-form-urlencoded' !== $contentType && + 'multipart/form-data' !== $contentType) { + return; } $postVars = $request->getPostData(); - if (!isset($postVars['sabreAction'])) + if (!isset($postVars['sabreAction'])) { return; + } $uri = $request->getPath(); if ($this->server->emit('onBrowserPostAction', [$uri, $postVars['sabreAction'], $postVars])) { - switch ($postVars['sabreAction']) { - - case 'mkcol' : + case 'mkcol': if (isset($postVars['name']) && trim($postVars['name'])) { // Using basename() because we won't allow slashes - list(, $folderName) = URLUtil::splitPath(trim($postVars['name'])); + list(, $folderName) = Uri\split(trim($postVars['name'])); if (isset($postVars['resourceType'])) { $resourceType = explode(',', $postVars['resourceType']); @@ -196,7 +198,7 @@ class Plugin extends DAV\ServerPlugin { foreach ($postVars as $varName => $varValue) { // Any _POST variable in clark notation is treated // like a property. - if ($varName[0] === '{') { + if ('{' === $varName[0]) { // PHP will convert any dots to underscores. // This leaves us with no way to differentiate // the two. @@ -212,65 +214,67 @@ class Plugin extends DAV\ServerPlugin { $resourceType, $properties ); - $this->server->createCollection($uri . '/' . $folderName, $mkCol); + $this->server->createCollection($uri.'/'.$folderName, $mkCol); } break; // @codeCoverageIgnoreStart - case 'put' : + case 'put': - if ($_FILES) $file = current($_FILES); - else break; + if ($_FILES) { + $file = current($_FILES); + } else { + break; + } - list(, $newName) = URLUtil::splitPath(trim($file['name'])); - if (isset($postVars['name']) && trim($postVars['name'])) + list(, $newName) = Uri\split(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); + list(, $newName) = Uri\split($newName); if (is_uploaded_file($file['tmp_name'])) { - $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'], 'r')); + $this->server->createFile($uri.'/'.$newName, fopen($file['tmp_name'], 'r')); } break; // @codeCoverageIgnoreEnd - } - } $response->setHeader('Location', $request->getUrl()); $response->setStatus(302); - return false; + return false; } /** * Escapes a string for html. * * @param string $value + * * @return string */ - function escapeHTML($value) { - + public function escapeHTML($value) + { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); - } /** - * Generates the html directory index for a given url + * Generates the html directory index for a given url. * * @param string $path + * * @return string */ - function generateDirectoryIndex($path) { - + public function generateDirectoryIndex($path) + { $html = $this->generateHeader($path ? $path : '/', $path); $node = $this->server->tree->getNodeForPath($path); if ($node instanceof DAV\ICollection) { - $html .= "

Nodes

\n"; - $html .= ""; + $html .= '
'; $subNodes = $this->server->getPropertiesForChildren($path, [ '{DAV:}displayname', @@ -281,10 +285,9 @@ class Plugin extends DAV\ServerPlugin { ]); foreach ($subNodes as $subPath => $subProps) { - $subNode = $this->server->tree->getNodeForPath($subPath); - $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($subPath); - list(, $displayPath) = URLUtil::splitPath($subPath); + $fullPath = $this->server->getBaseUri().HTTP\encodePath($subPath); + list(, $displayPath) = Uri\split($subPath); $subNodes[$subPath]['subNode'] = $subNode; $subNodes[$subPath]['fullPath'] = $fullPath; @@ -295,43 +298,46 @@ class Plugin extends DAV\ServerPlugin { foreach ($subNodes as $subProps) { $type = [ 'string' => 'Unknown', - 'icon' => 'cog', + 'icon' => 'cog', ]; if (isset($subProps['{DAV:}resourcetype'])) { $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']); } $html .= ''; - $html .= ''; - $html .= ''; + $html .= ''; + $html .= ''; $html .= ''; $buttonActions = ''; if ($subProps['subNode'] instanceof DAV\IFile) { - $buttonActions = ''; + $buttonActions = ''; } $this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]); - $html .= ''; + $html .= ''; $html .= ''; } $html .= '
' . $this->escapeHTML($subProps['displayPath']) . '' . $this->escapeHTML($type['string']) . ' '.$this->escapeHTML($subProps['displayPath']).''.$this->escapeHTML($type['string']).''; if (isset($subProps['{DAV:}getcontentlength'])) { - $html .= $this->escapeHTML($subProps['{DAV:}getcontentlength'] . ' bytes'); + $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 .= ''; + if (isset($subProps['{DAV:}displayname'])) { + $html .= $this->escapeHTML($subProps['{DAV:}displayname']); + } $html .= '' . $buttonActions . ''.$buttonActions.'
'; - } - $html .= "
"; - $html .= "

Properties

"; - $html .= ""; + $html .= ''; + $html .= '

Properties

'; + $html .= '
'; // Allprops request $propFind = new PropFindAll($path); @@ -343,12 +349,10 @@ class Plugin extends DAV\ServerPlugin { if (!in_array($propName, $this->uninterestingProperties)) { $html .= $this->drawPropertyRow($propName, $propValue); } - } - - $html .= "
"; - $html .= "
"; + $html .= ''; + $html .= ''; /* Start of generating actions */ @@ -358,8 +362,7 @@ class Plugin extends DAV\ServerPlugin { } if ($output) { - - $html .= "

Actions

"; + $html .= '

Actions

'; $html .= "
\n"; $html .= $output; $html .= "
\n"; @@ -371,7 +374,6 @@ class Plugin extends DAV\ServerPlugin { $this->server->httpResponse->setHeader('Content-Security-Policy', "default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';"); return $html; - } /** @@ -379,31 +381,30 @@ class Plugin extends DAV\ServerPlugin { * * @return string */ - function generatePluginListing() { - + public function generatePluginListing() + { $html = $this->generateHeader('Plugins'); - $html .= "

Plugins

"; - $html .= ""; + $html .= '

Plugins

'; + $html .= '
'; foreach ($this->server->getPlugins() as $plugin) { $info = $plugin->getPluginInfo(); - $html .= ''; - $html .= ''; + $html .= ''; + $html .= ''; $html .= ''; } - $html .= "
' . $info['name'] . '' . $info['description'] . '
'.$info['name'].''.$info['description'].''; if (isset($info['link']) && $info['link']) { - $html .= ''; + $html .= ''; } $html .= '
"; - $html .= "
"; + $html .= ''; + $html .= '
'; /* Start of generating actions */ $html .= $this->generateFooter(); return $html; - } /** @@ -414,22 +415,23 @@ class Plugin extends DAV\ServerPlugin { * * @param string $title * @param string $path + * * @return string */ - function generateHeader($title, $path = null) { - + public function generateHeader($title, $path = null) + { $version = ''; if (DAV\Server::$exposeVersion) { $version = DAV\Version::VERSION; } $vars = [ - 'title' => $this->escapeHTML($title), - 'favicon' => $this->escapeHTML($this->getAssetUrl('favicon.ico')), - 'style' => $this->escapeHTML($this->getAssetUrl('sabredav.css')), + 'title' => $this->escapeHTML($title), + '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(), + 'logo' => $this->escapeHTML($this->getAssetUrl('sabredav.png')), + 'baseUrl' => $this->server->getBaseUri(), ]; $html = <<server->getBaseUri() . URLUtil::encodePath($parentUri); - $html .= '⇤ Go to parent'; + if ($path) { + list($parentUri) = Uri\split($path); + $fullPath = $this->server->getBaseUri().HTTP\encodePath($parentUri); + $html .= '⇤ Go to parent'; } else { $html .= '⇤ Go to parent'; } $html .= ' Plugins'; - $html .= ""; + $html .= ''; return $html; - } /** @@ -476,18 +477,19 @@ HTML; * * @return string */ - function generateFooter() { - + public function generateFooter() + { $version = ''; if (DAV\Server::$exposeVersion) { $version = DAV\Version::VERSION; } + $year = date('Y'); + return <<Generated by SabreDAV $version (c)2007-2016 http://sabre.io/ + HTML; - } /** @@ -498,19 +500,20 @@ HTML; * creating new directories. * * @param DAV\INode $node - * @param mixed $output - * @param string $path - * @return void + * @param mixed $output + * @param string $path */ - function htmlActionsPanel(DAV\INode $node, &$output, $path) { - - if (!$node instanceof DAV\ICollection) + public function htmlActionsPanel(DAV\INode $node, &$output, $path) + { + 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') + if ('Sabre\\DAV\\SimpleCollection' === get_class($node)) { return; + } $output .= << @@ -527,7 +530,6 @@ HTML; HTML; - } /** @@ -535,32 +537,35 @@ HTML; * suiteable for http access. * * @param string $assetName + * * @return string */ - protected function getAssetUrl($assetName) { - - return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); - + 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 + * * @throws DAV\Exception\NotFound + * * @return string */ - protected function getLocalAssetPath($assetName) { - - $assetDir = __DIR__ . '/assets/'; - $path = $assetDir . $assetName; + 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, '/') === '/..') { + if (false !== strpos($path, '/../') || '/..' === 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)) { + $realPath = realpath($path); + if ($realPath && 0 === strpos($realPath, realpath($assetDir)) && file_exists($path)) { return $path; } throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); @@ -570,10 +575,9 @@ HTML; * This method reads an asset from disk and generates a full http response. * * @param string $assetName - * @return void */ - protected function serveAsset($assetName) { - + protected function serveAsset($assetName) + { $assetPath = $this->getLocalAssetPath($assetName); // Rudimentary mime type detection @@ -594,7 +598,6 @@ HTML; $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); $this->server->httpResponse->setStatus(200); $this->server->httpResponse->setBody(fopen($assetPath, 'r')); - } /** @@ -603,10 +606,11 @@ HTML; * * @param array $a * @param array $b + * * @return int */ - protected function compareNodes($a, $b) { - + protected function compareNodes($a, $b) + { $typeA = (isset($a['{DAV:}resourcetype'])) ? (in_array('{DAV:}collection', $a['{DAV:}resourcetype']->getValue())) : false; @@ -619,29 +623,30 @@ HTML; if ($typeA === $typeB) { return strnatcasecmp($a['displayPath'], $b['displayPath']); } - return (($typeA < $typeB) ? 1 : -1); + return ($typeA < $typeB) ? 1 : -1; } /** * Maps a resource type to a human-readable string and icon. * - * @param array $resourceTypes + * @param array $resourceTypes * @param DAV\INode $node + * * @return array */ - private function mapResourceType(array $resourceTypes, $node) { - + private function mapResourceType(array $resourceTypes, $node) + { if (!$resourceTypes) { if ($node instanceof DAV\IFile) { return [ 'string' => 'File', - 'icon' => 'file', + 'icon' => 'file', ]; } else { return [ 'string' => 'Unknown', - 'icon' => 'cog', + 'icon' => 'cog', ]; } } @@ -649,53 +654,53 @@ HTML; $types = [ '{http://calendarserver.org/ns/}calendar-proxy-write' => [ 'string' => 'Proxy-Write', - 'icon' => 'people', + 'icon' => 'people', ], '{http://calendarserver.org/ns/}calendar-proxy-read' => [ 'string' => 'Proxy-Read', - 'icon' => 'people', + 'icon' => 'people', ], '{urn:ietf:params:xml:ns:caldav}schedule-outbox' => [ 'string' => 'Outbox', - 'icon' => 'inbox', + 'icon' => 'inbox', ], '{urn:ietf:params:xml:ns:caldav}schedule-inbox' => [ 'string' => 'Inbox', - 'icon' => 'inbox', + 'icon' => 'inbox', ], '{urn:ietf:params:xml:ns:caldav}calendar' => [ 'string' => 'Calendar', - 'icon' => 'calendar', + 'icon' => 'calendar', ], '{http://calendarserver.org/ns/}shared-owner' => [ 'string' => 'Shared', - 'icon' => 'calendar', + 'icon' => 'calendar', ], '{http://calendarserver.org/ns/}subscribed' => [ 'string' => 'Subscription', - 'icon' => 'calendar', + 'icon' => 'calendar', ], '{urn:ietf:params:xml:ns:carddav}directory' => [ 'string' => 'Directory', - 'icon' => 'globe', + 'icon' => 'globe', ], '{urn:ietf:params:xml:ns:carddav}addressbook' => [ 'string' => 'Address book', - 'icon' => 'book', + 'icon' => 'book', ], '{DAV:}principal' => [ 'string' => 'Principal', - 'icon' => 'person', + 'icon' => 'person', ], '{DAV:}collection' => [ 'string' => 'Collection', - 'icon' => 'folder', + 'icon' => 'folder', ], ]; $info = [ 'string' => [], - 'icon' => 'cog', + 'icon' => 'cog', ]; foreach ($resourceTypes as $k => $resourceType) { if (isset($types[$resourceType])) { @@ -713,42 +718,41 @@ HTML; $info['string'] = implode(', ', $info['string']); return $info; - } /** - * Draws a table row for a property + * Draws a table row for a property. * * @param string $name - * @param mixed $value + * @param mixed $value + * * @return string */ - private function drawPropertyRow($name, $value) { - + private function drawPropertyRow($name, $value) + { $html = new HtmlOutputHelper( $this->server->getBaseUri(), $this->server->xml->namespaceMap ); - return "" . $html->xmlName($name) . "" . $this->drawPropertyValue($html, $value) . ""; - + return ''.$html->xmlName($name).''.$this->drawPropertyValue($html, $value).''; } /** - * Draws a table row for a property + * Draws a table row for a property. * * @param HtmlOutputHelper $html - * @param mixed $value + * @param mixed $value + * * @return string */ - private function drawPropertyValue($html, $value) { - + private function drawPropertyValue($html, $value) + { if (is_scalar($value)) { return $html->h($value); } elseif ($value instanceof HtmlOutput) { return $value->toHtml($html); } elseif ($value instanceof \Sabre\Xml\XmlSerializable) { - // There's no default html output for this property, we're going // to output the actual xml serialization instead. $xml = $this->server->xml->write('{DAV:}root', $value, $this->server->getBaseUri()); @@ -756,12 +760,11 @@ HTML; // element. $xml = explode("\n", $xml); $xml = array_slice($xml, 2, -2); - return "
" . $html->h(implode("\n", $xml)) . "
"; + return '
'.$html->h(implode("\n", $xml)).'
'; } else { - return "unknown"; + return 'unknown'; } - } /** @@ -772,10 +775,9 @@ HTML; * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'browser'; - } /** @@ -789,14 +791,12 @@ HTML; * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Generates HTML indexes and debug information for your sabre/dav server', - 'link' => 'http://sabre.io/dav/browser-plugin/', + 'link' => 'http://sabre.io/dav/browser-plugin/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/PropFindAll.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/PropFindAll.php index c14b7f2f96d..34702bdd8aa 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/PropFindAll.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Browser/PropFindAll.php @@ -1,5 +1,7 @@ result[$propertyName] = [200, $value]; } - } /** - * Sets the value of the property + * 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 + * @param mixed $value + * @param int $status */ - function set($propertyName, $value, $status = null) { - + public 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) { - + public function get($propertyName) + { return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null; - } /** @@ -99,12 +96,12 @@ class PropFindAll extends PropFind { * null will be returned. * * @param string $propertyName + * * @return int|null */ - function getStatus($propertyName) { - + public function getStatus($propertyName) + { return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : 404; - } /** @@ -113,11 +110,11 @@ class PropFindAll extends PropFind { * * @return array */ - function get404Properties() { - + public function get404Properties() + { $result = []; foreach ($this->result as $propertyName => $stuff) { - if ($stuff[0] === 404) { + if (404 === $stuff[0]) { $result[] = $propertyName; } } @@ -125,8 +122,7 @@ class PropFindAll extends PropFind { if (!$result) { $result[] = '{http://sabredav.org/ns}idk'; } + return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Client.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Client.php index 175ad1bc459..cfa24cd2992 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Client.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Client.php @@ -1,12 +1,14 @@ xml->elementMap. * It's deprecated as of version 3.0.0, and should no longer be used. * * @deprecated + * * @var array */ public $propertyMap = []; /** - * Base URI + * Base URI. * * This URI will be used to resolve relative urls. * @@ -49,17 +52,17 @@ class Client extends HTTP\Client { protected $baseUri; /** - * Basic authentication + * Basic authentication. */ const AUTH_BASIC = 1; /** - * Digest authentication + * Digest authentication. */ const AUTH_DIGEST = 2; /** - * NTLM authentication + * NTLM authentication. */ const AUTH_NTLM = 4; @@ -69,12 +72,12 @@ class Client extends HTTP\Client { const ENCODING_IDENTITY = 1; /** - * Deflate encoding + * Deflate encoding. */ const ENCODING_DEFLATE = 2; /** - * Gzip encoding + * Gzip encoding. */ const ENCODING_GZIP = 4; @@ -84,14 +87,14 @@ class Client extends HTTP\Client { const ENCODING_ALL = 7; /** - * Content-encoding + * Content-encoding. * * @var int */ protected $encoding = self::ENCODING_IDENTITY; /** - * Constructor + * Constructor. * * Settings are provided through the 'settings' argument. The following * settings are supported: @@ -112,8 +115,8 @@ class Client extends HTTP\Client { * * @param array $settings */ - function __construct(array $settings) { - + public function __construct(array $settings) + { if (!isset($settings['baseUri'])) { throw new \InvalidArgumentException('A baseUri must be provided'); } @@ -146,8 +149,7 @@ class Client extends HTTP\Client { } $this->addCurlSetting(CURLOPT_HTTPAUTH, $curlType); - $this->addCurlSetting(CURLOPT_USERPWD, $userName . ':' . $password); - + $this->addCurlSetting(CURLOPT_USERPWD, $userName.':'.$password); } if (isset($settings['encoding'])) { @@ -166,16 +168,15 @@ class Client extends HTTP\Client { $this->addCurlSetting(CURLOPT_ENCODING, implode(',', $encodings)); } - $this->addCurlSetting(CURLOPT_USERAGENT, 'sabre-dav/' . Version::VERSION . ' (http://sabre.io/)'); + $this->addCurlSetting(CURLOPT_USERAGENT, 'sabre-dav/'.Version::VERSION.' (http://sabre.io/)'); $this->xml = new Xml\Service(); // BC - $this->propertyMap = & $this->xml->elementMap; - + $this->propertyMap = &$this->xml->elementMap; } /** - * Does a PROPFIND request + * Does a PROPFIND request. * * The list of requested properties must be specified as an array, in clark * notation. @@ -191,28 +192,28 @@ class Client extends HTTP\Client { * made to the server to also return all child resources. * * @param string $url - * @param array $properties - * @param int $depth + * @param array $properties + * @param int $depth + * * @return array */ - function propFind($url, array $properties, $depth = 0) { - + public 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 ) = \Sabre\Xml\Service::parseClarkNotation($property); - if ($namespace === 'DAV:') { - $element = $dom->createElement('d:' . $elementName); + if ('DAV:' === $namespace) { + $element = $dom->createElement('d:'.$elementName); } else { - $element = $dom->createElementNS($namespace, 'x:' . $elementName); + $element = $dom->createElementNS($namespace, 'x:'.$elementName); } $prop->appendChild($element); @@ -224,49 +225,48 @@ class Client extends HTTP\Client { $url = $this->getAbsoluteUrl($url); $request = new HTTP\Request('PROPFIND', $url, [ - 'Depth' => $depth, - 'Content-Type' => 'application/xml' + 'Depth' => $depth, + 'Content-Type' => 'application/xml', ], $body); $response = $this->send($request); - if ((int)$response->getStatus() >= 400) { + if ((int) $response->getStatus() >= 400) { throw new HTTP\ClientHttpException($response); } $result = $this->parseMultiStatus($response->getBodyAsString()); // If depth was 0, we only return the top item - if ($depth === 0) { + if (0 === $depth) { 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 + * 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 + * @param array $properties + * * @return bool */ - function propPatch($url, array $properties) { - + public function propPatch($url, array $properties) + { $propPatch = new Xml\Request\PropPatch(); $propPatch->properties = $properties; $xml = $this->xml->write( @@ -284,7 +284,7 @@ class Client extends HTTP\Client { throw new HTTP\ClientHttpException($response); } - if ($response->getStatus() === 207) { + if (207 === $response->getStatus()) { // If it's a 207, the request could still have failed, but the // information is hidden in the response body. $result = $this->parseMultiStatus($response->getBodyAsString()); @@ -292,26 +292,23 @@ class Client extends HTTP\Client { $errorProperties = []; foreach ($result as $href => $statusList) { foreach ($statusList as $status => $properties) { - if ($status >= 400) { foreach ($properties as $propName => $propValue) { - $errorProperties[] = $propName . ' (' . $status . ')'; + $errorProperties[] = $propName.' ('.$status.')'; } } - } } if ($errorProperties) { - - throw new HTTP\ClientException('PROPPATCH failed. The following properties errored: ' . implode(', ', $errorProperties)); + throw new HTTP\ClientException('PROPPATCH failed. The following properties errored: '.implode(', ', $errorProperties)); } } - return true; + return true; } /** - * Performs an HTTP options 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 @@ -319,8 +316,8 @@ class Client extends HTTP\Client { * * @return array */ - function options() { - + public function options() + { $request = new HTTP\Request('OPTIONS', $this->getAbsoluteUrl('')); $response = $this->send($request); @@ -333,8 +330,8 @@ class Client extends HTTP\Client { foreach ($features as &$v) { $v = trim($v); } - return $features; + return $features; } /** @@ -359,24 +356,26 @@ class Client extends HTTP\Client { * Note that it is no longer recommended to use this method, use the send() * method instead. * - * @param string $method - * @param string $url + * @param string $method + * @param string $url * @param string|resource|null $body - * @param array $headers - * @throws ClientException, in case a curl error occurred. + * @param array $headers + * + * @throws clientException, in case a curl error occurred + * * @return array */ - function request($method, $url = '', $body = null, array $headers = []) { - + public 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()), - ]; + return [ + 'body' => $response->getBodyAsString(), + 'statusCode' => (int) $response->getStatus(), + 'headers' => array_change_key_case($response->getHeaders()), + ]; } /** @@ -384,19 +383,19 @@ class Client extends HTTP\Client { * urls are expanded based on the base url as given by the server. * * @param string $url + * * @return string */ - function getAbsoluteUrl($url) { - + public function getAbsoluteUrl($url) + { return Uri\resolve( $this->baseUri, $url ); - } /** - * Parses a WebDAV multistatus response body + * Parses a WebDAV multistatus response body. * * This method returns an array with the following structure * @@ -418,22 +417,19 @@ class Client extends HTTP\Client { * * * @param string $body xml body + * * @return array */ - function parseMultiStatus($body) { - + public function parseMultiStatus($body) + { $multistatus = $this->xml->expect('{DAV:}multistatus', $body); $result = []; foreach ($multistatus->getResponses() as $response) { - $result[$response->getHref()] = $response->getResponseProperties(); - } return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Collection.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Collection.php index 35c90b5afae..2728bb27ed9 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Collection.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Collection.php @@ -1,9 +1,11 @@ getChildren() as $child) { - - if ($child->getName() === $name) return $child; - + if ($child->getName() === $name) { + return $child; + } } - throw new Exception\NotFound('File not found: ' . $name); - + throw new Exception\NotFound('File not found: '.$name); } /** @@ -45,25 +48,22 @@ abstract class Collection extends Node implements ICollection { * It is generally a good idea to try and override this. Usually it can be optimized. * * @param string $name + * * @return bool */ - function childExists($name) { - + public function childExists($name) + { try { - $this->getChild($name); + return true; - } catch (Exception\NotFound $e) { - return false; - } - } /** - * Creates a new file in the directory + * 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. @@ -82,28 +82,25 @@ abstract class Collection extends Node implements ICollection { * 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 string $name Name of the file * @param resource|string $data Initial payload - * @return null|string + * + * @return string|null */ - function createFile($name, $data = null) { - - throw new Exception\Forbidden('Permission denied to create file (filename ' . $name . ')'); - + public function createFile($name, $data = null) + { + throw new Exception\Forbidden('Permission denied to create file (filename '.$name.')'); } /** - * Creates a new subdirectory + * Creates a new subdirectory. * * @param string $name + * * @throws Exception\Forbidden - * @return void */ - function createDirectory($name) { - + public function createDirectory($name) + { throw new Exception\Forbidden('Permission denied to create directory'); - } - - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/CorePlugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/CorePlugin.php index 676cdd04a29..ef1dfceb65b 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/CorePlugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/CorePlugin.php @@ -1,5 +1,7 @@ server = $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: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('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); - - $server->on('exception', [$this, 'exception']); + $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); + $server->on('exception', [$this, 'exception']); } /** @@ -62,34 +62,40 @@ class CorePlugin extends ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'core'; - } /** * This is the default implementation for the GET method. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpGet(RequestInterface $request, ResponseInterface $response) { - + public function httpGet(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path); - if (!$node instanceof IFile) return; + if (!$node instanceof IFile) { + return; + } - $body = $node->get(); + if ('HEAD' === $request->getHeader('X-Sabre-Original-Method')) { + $body = ''; + } else { + $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; + // Converting string into stream, if needed. + if (is_string($body)) { + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $body); + rewind($stream); + $body = $stream; + } } /* @@ -105,14 +111,11 @@ class CorePlugin extends ServerPlugin { $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; } @@ -126,7 +129,6 @@ class CorePlugin extends ServerPlugin { // 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 { @@ -134,81 +136,87 @@ class CorePlugin extends ServerPlugin { // It's a date. We must check if the entity is modified since // the specified date. - if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; - else { + if (!isset($httpHeaders['Last-Modified'])) { + $ignoreRangeHeader = true; + } else { $modified = new \DateTime($httpHeaders['Last-Modified']); - if ($modified > $ifRangeDate) $ignoreRangeHeader = true; + 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; + 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; - + 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; - + if ($start < 0) { + $start = 0; + } } // Streams may advertise themselves as seekable, but still not // actually allow fseek. We'll manually go forward in the stream // if fseek failed. - if (!stream_get_meta_data($body)['seekable'] || fseek($body, $start, SEEK_SET) === -1) { + if (!stream_get_meta_data($body)['seekable'] || -1 === fseek($body, $start, SEEK_SET)) { $consumeBlock = 8192; - for ($consumed = 0; $start - $consumed > 0;){ - if (feof($body)) throw new Exception\RequestedRangeNotSatisfiable('The start offset (' . $start . ') exceeded the size of the entity (' . $consumed . ')'); + for ($consumed = 0; $start - $consumed > 0;) { + if (feof($body)) { + throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$start.') exceeded the size of the entity ('.$consumed.')'); + } $consumed += strlen(fread($body, min($start - $consumed, $consumeBlock))); } } $response->setHeader('Content-Length', $end - $start + 1); - $response->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $nodeSize); + $response->setHeader('Content-Range', 'bytes '.$start.'-'.$end.'/'.$nodeSize); $response->setStatus(206); $response->setBody($body); - } else { - - if ($nodeSize) $response->setHeader('Content-Length', $nodeSize); + if ($nodeSize) { + $response->setHeader('Content-Length', $nodeSize); + } $response->setStatus(200); $response->setBody($body); - } // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** - * HTTP OPTIONS + * HTTP OPTIONS. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpOptions(RequestInterface $request, ResponseInterface $response) { - + public function httpOptions(RequestInterface $request, ResponseInterface $response) + { $methods = $this->server->getAllowedMethods($request->getPath()); $response->setHeader('Allow', strtoupper(implode(', ', $methods))); @@ -227,31 +235,31 @@ class CorePlugin extends ServerPlugin { // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** - * HTTP HEAD + * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpHead(RequestInterface $request, ResponseInterface $response) { - + public function httpHead(RequestInterface $request, ResponseInterface $response) + { // This is implemented by changing the HEAD request to a GET request, - // and dropping the response body. + // and telling the request handler that is doesn't need to create the body. $subRequest = clone $request; $subRequest->setMethod('GET'); + $subRequest->setHeader('X-Sabre-Original-Method', 'HEAD'); try { $this->server->invokeMethod($subRequest, $response, false); - $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, @@ -267,23 +275,23 @@ class CorePlugin extends ServerPlugin { // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** - * HTTP Delete + * HTTP Delete. * * The HTTP delete method, deletes a given uri * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function httpDelete(RequestInterface $request, ResponseInterface $response) { - + public function httpDelete(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); - if (!$this->server->emit('beforeUnbind', [$path])) return false; + if (!$this->server->emit('beforeUnbind', [$path])) { + return false; + } $this->server->tree->delete($path); $this->server->emit('afterUnbind', [$path]); @@ -293,11 +301,10 @@ class CorePlugin extends ServerPlugin { // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** - * WebDAV PROPFIND + * 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 @@ -308,12 +315,11 @@ class CorePlugin extends ServerPlugin { * * It has to return a HTTP 207 Multi-status status code * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function httpPropFind(RequestInterface $request, ResponseInterface $response) { - + public function httpPropFind(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); $requestBody = $request->getBodyAsString(); @@ -321,7 +327,7 @@ class CorePlugin extends ServerPlugin { try { $propFindXml = $this->server->xml->expect('{DAV:}propfind', $requestBody); } catch (ParseException $e) { - throw new BadRequest($e->getMessage(), null, $e); + throw new BadRequest($e->getMessage(), 0, $e); } } else { $propFindXml = new Xml\Request\PropFind(); @@ -331,7 +337,9 @@ class CorePlugin extends ServerPlugin { $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; + if (!$this->server->enablePropfindDepthInfinity && 0 != $depth) { + $depth = 1; + } $newProperties = $this->server->getPropertiesIteratorForPath($path, $propFindXml->properties, $depth); @@ -350,7 +358,7 @@ class CorePlugin extends ServerPlugin { $response->setHeader('DAV', implode(', ', $features)); $prefer = $this->server->getHTTPPrefer(); - $minimal = $prefer['return'] === 'minimal'; + $minimal = 'minimal' === $prefer['return']; $data = $this->server->generateMultiStatus($newProperties, $minimal); $response->setBody($data); @@ -358,27 +366,27 @@ class CorePlugin extends ServerPlugin { // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** - * WebDAV PROPPATCH + * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpPropPatch(RequestInterface $request, ResponseInterface $response) { - + public function httpPropPatch(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); try { $propPatch = $this->server->xml->expect('{DAV:}propertyupdate', $request->getBody()); } catch (ParseException $e) { - throw new BadRequest($e->getMessage(), null, $e); + throw new BadRequest($e->getMessage(), 0, $e); } $newProperties = $propPatch->properties; @@ -387,31 +395,27 @@ class CorePlugin extends ServerPlugin { $prefer = $this->server->getHTTPPrefer(); $response->setHeader('Vary', 'Brief,Prefer'); - if ($prefer['return'] === 'minimal') { - + if ('minimal' === $prefer['return']) { // If return-minimal is specified, we only have to check if the // request was successful, and don't need to return the // multi-status. $ok = true; foreach ($result as $prop => $code) { - if ((int)$code > 299) { + 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) { @@ -430,22 +434,22 @@ class CorePlugin extends ServerPlugin { // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** - * HTTP PUT method + * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpPut(RequestInterface $request, ResponseInterface $response) { - + public function httpPut(RequestInterface $request, ResponseInterface $response) + { $body = $request->getBodyAsStream(); $path = $request->getPath(); @@ -463,7 +467,6 @@ class CorePlugin extends ServerPlugin { // 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. @@ -487,7 +490,7 @@ class CorePlugin extends ServerPlugin { // Only reading first byte $firstByte = fread($body, 1); - if (strlen($firstByte) !== 1) { + if (1 !== strlen($firstByte)) { throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); } @@ -500,26 +503,25 @@ class CorePlugin extends ServerPlugin { 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 (!($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); + 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)) { @@ -528,61 +530,55 @@ class CorePlugin extends ServerPlugin { } $response->setHeader('Content-Length', '0'); - if ($etag) $response->setHeader('ETag', $etag); + if ($etag) { + $response->setHeader('ETag', $etag); + } $response->setStatus(201); - } // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } - /** - * WebDAV MKCOL + * WebDAV MKCOL. * * The MKCOL method is used to create a new collection (directory) on the server * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpMkcol(RequestInterface $request, ResponseInterface $response) { - + public 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) { - + if (null === $contentType || (0 !== strpos($contentType, 'application/xml') && 0 !== strpos($contentType, 'text/xml'))) { // 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'); - } try { $mkcol = $this->server->xml->expect('{DAV:}mkcol', $requestBody); } catch (\Sabre\Xml\ParseException $e) { - throw new Exception\BadRequest($e->getMessage(), null, $e); + throw new Exception\BadRequest($e->getMessage(), 0, $e); } $properties = $mkcol->getProperties(); - if (!isset($properties['{DAV:}resourcetype'])) + 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']; - } $mkcol = new MkCol($resourceType, $properties); @@ -596,7 +592,6 @@ class CorePlugin extends ServerPlugin { $response->setBody( $this->server->generateMultiStatus([$result]) ); - } else { $response->setHeader('Content-Length', '0'); $response->setStatus(201); @@ -605,38 +600,42 @@ class CorePlugin extends ServerPlugin { // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** - * WebDAV HTTP MOVE method + * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpMove(RequestInterface $request, ResponseInterface $response) { - + public 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', [$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 (!$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']); @@ -656,28 +655,32 @@ class CorePlugin extends ServerPlugin { // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** - * WebDAV HTTP COPY method + * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpCopy(RequestInterface $request, ResponseInterface $response) { - + public function httpCopy(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); $copyInfo = $this->server->getCopyAndMoveInfo($request); - if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false; + if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) { + return false; + } if ($copyInfo['destinationExists']) { - if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) return false; + if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) { + return false; + } $this->server->tree->delete($copyInfo['destination']); } @@ -691,22 +694,21 @@ class CorePlugin extends ServerPlugin { // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - - } /** - * HTTP REPORT method implementation + * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpReport(RequestInterface $request, ResponseInterface $response) { - + public function httpReport(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); $result = $this->server->xml->parse( @@ -716,16 +718,13 @@ class CorePlugin extends ServerPlugin { ); if ($this->server->emit('report', [$rootElementName, $result, $path])) { - // If emit returned true, it means the report was not supported throw new Exception\ReportNotSupported(); - } // Sending back false will interrupt the event chain and tell the server // we've handled this method. return false; - } /** @@ -734,12 +733,11 @@ class CorePlugin extends ServerPlugin { * 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 string $path * @param PropPatch $propPatch - * @return void */ - function propPatchProtectedPropertyCheck($path, PropPatch $propPatch) { - + public function propPatchProtectedPropertyCheck($path, PropPatch $propPatch) + { // Comparing the mutation list to the list of protected properties. $mutations = $propPatch->getMutations(); @@ -751,7 +749,6 @@ class CorePlugin extends ServerPlugin { if ($protected) { $propPatch->setResultCode($protected, 403); } - } /** @@ -760,19 +757,17 @@ class CorePlugin extends ServerPlugin { * Here we check if a node implements IProperties and let the node handle * updating of (some) properties. * - * @param string $path + * @param string $path * @param PropPatch $propPatch - * @return void */ - function propPatchNodeUpdate($path, PropPatch $propPatch) { - + public 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); } - } /** @@ -781,12 +776,11 @@ class CorePlugin extends ServerPlugin { * Here we add all the default properties. * * @param PropFind $propFind - * @param INode $node - * @return void + * @param INode $node */ - function propFind(PropFind $propFind, INode $node) { - - $propFind->handle('{DAV:}getlastmodified', function() use ($node) { + public function propFind(PropFind $propFind, INode $node) + { + $propFind->handle('{DAV:}getlastmodified', function () use ($node) { $lm = $node->getLastModified(); if ($lm) { return new Xml\Property\GetLastModified($lm); @@ -801,34 +795,36 @@ class CorePlugin extends ServerPlugin { if ($node instanceof IQuota) { $quotaInfo = null; - $propFind->handle('{DAV:}quota-used-bytes', function() use (&$quotaInfo, $node) { + $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) { + $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) { + $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 Xml\Property\SupportedReportSet($reports); }); - $propFind->handle('{DAV:}resourcetype', function() use ($node) { + $propFind->handle('{DAV:}resourcetype', function () use ($node) { return new Xml\Property\ResourceType($this->server->getResourceTypeForNode($node)); }); - $propFind->handle('{DAV:}supported-method-set', function() use ($propFind) { + $propFind->handle('{DAV:}supported-method-set', function () use ($propFind) { return new Xml\Property\SupportedMethodSet( $this->server->getAllowedMethods($propFind->getPath()) ); }); - } /** @@ -838,20 +834,16 @@ class CorePlugin extends ServerPlugin { * populate the result. * * @param PropFind $propFind - * @param INode $node - * @return void + * @param INode $node */ - function propFindNode(PropFind $propFind, INode $node) { - + public function propFindNode(PropFind $propFind, INode $node) + { if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) { - $nodeProperties = $node->getProperties($propertyNames); foreach ($nodeProperties as $propertyName => $propertyValue) { $propFind->set($propertyName, $propertyValue, 200); } - } - } /** @@ -861,17 +853,17 @@ class CorePlugin extends ServerPlugin { * want other systems to first have a chance to handle the properties. * * @param PropFind $propFind - * @param INode $node - * @return void + * @param INode $node */ - function propFindLate(PropFind $propFind, INode $node) { - - $propFind->handle('{http://calendarserver.org/ns/}getctag', function() use ($propFind) { - + public 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; + if ($val) { + return $val; + } $val = $propFind->get('{DAV:}sync-token'); if ($val && is_scalar($val)) { @@ -899,9 +891,7 @@ class CorePlugin extends ServerPlugin { return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); } } - }); - } /** @@ -909,8 +899,8 @@ class CorePlugin extends ServerPlugin { * * @param Exception $e */ - function exception($e) { - + public function exception($e) + { $logLevel = \Psr\Log\LogLevel::CRITICAL; if ($e instanceof \Sabre\DAV\Exception) { // If it's a standard sabre/dav exception, it means we have a http @@ -947,13 +937,12 @@ class CorePlugin extends ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'The Core plugin provides a lot of the basic functionality required by WebDAV, such as a default implementation for all HTTP and WebDAV methods.', - 'link' => null, + 'link' => null, ]; - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception.php index 14f5bab2a53..e1d9906234b 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception.php @@ -1,5 +1,7 @@ lock) { $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:no-conflicting-lock'); $errorNode->appendChild($error); $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:', 'd:href', $this->lock->uri)); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Forbidden.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Forbidden.php index 77df7ca9ecc..2f882c39650 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Forbidden.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Forbidden.php @@ -1,11 +1,13 @@ ownerDocument->createElementNS('DAV:', 'd:valid-resourcetype'); $errorNode->appendChild($error); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php index 51a253b296b..6c5f1c4358e 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php @@ -1,11 +1,13 @@ ownerDocument->createElementNS('DAV:', 'd:valid-sync-token'); $errorNode->appendChild($error); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/LengthRequired.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/LengthRequired.php index 98971855834..9d26fcb105a 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/LengthRequired.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/LengthRequired.php @@ -1,11 +1,13 @@ message = 'The locktoken supplied does not match any locks on this entity'; - + public function __construct() + { + parent::__construct('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 + * This method allows the exception to include additional information into the WebDAV error response. * - * @param DAV\Server $server + * @param DAV\Server $server * @param \DOMElement $errorNode - * @return void */ - function serialize(DAV\Server $server, \DOMElement $errorNode) { - + public function serialize(DAV\Server $server, \DOMElement $errorNode) + { $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:lock-token-matches-request-uri'); $errorNode->appendChild($error); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Locked.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Locked.php index 8176db46e87..632bafc6081 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Locked.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/Locked.php @@ -1,11 +1,13 @@ lock = $lock; - } /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception. * * @return int */ - function getHTTPCode() { - + public function getHTTPCode() + { return 423; - } /** - * This method allows the exception to include additional information into the WebDAV error response + * This method allows the exception to include additional information into the WebDAV error response. * - * @param DAV\Server $server + * @param DAV\Server $server * @param \DOMElement $errorNode - * @return void */ - function serialize(DAV\Server $server, \DOMElement $errorNode) { - + public function serialize(DAV\Server $server, \DOMElement $errorNode) + { if ($this->lock) { $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:lock-token-submitted'); $errorNode->appendChild($error); @@ -66,7 +67,5 @@ class Locked extends DAV\Exception { $href ); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php index 30c1c255345..a3d9c56f2d4 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php @@ -1,11 +1,13 @@ getAllowedMethods($server->getRequestUri()); return [ 'Allow' => strtoupper(implode(', ', $methods)), ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/NotAuthenticated.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/NotAuthenticated.php index e69a60c7549..0a5ba9b5abd 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/NotAuthenticated.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/NotAuthenticated.php @@ -1,11 +1,13 @@ header = $header; - } /** - * Returns the HTTP statuscode for this exception + * Returns the HTTP statuscode for this exception. * * @return int */ - function getHTTPCode() { - + public function getHTTPCode() + { return 412; - } /** - * This method allows the exception to include additional information into the WebDAV error response + * This method allows the exception to include additional information into the WebDAV error response. * - * @param DAV\Server $server + * @param DAV\Server $server * @param \DOMElement $errorNode - * @return void */ - function serialize(DAV\Server $server, \DOMElement $errorNode) { - + public function serialize(DAV\Server $server, \DOMElement $errorNode) + { if ($this->header) { $prop = $errorNode->ownerDocument->createElement('s:header'); $prop->nodeValue = $this->header; $errorNode->appendChild($prop); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/ReportNotSupported.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/ReportNotSupported.php index a8369562702..cecfec12d9b 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/ReportNotSupported.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/ReportNotSupported.php @@ -1,11 +1,13 @@ ownerDocument->createElementNS('DAV:', 'd:supported-report'); $errorNode->appendChild($error); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php index c8ccfc062a9..6ccb5b8c88b 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php @@ -1,11 +1,13 @@ ownerDocument->createElementNS('DAV:', 'd:number-of-matches-within-limits'); $errorNode->appendChild($error); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php index f3d92842da5..bc9da30dae5 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php @@ -1,11 +1,13 @@ path . '/' . $name; + public function createFile($name, $data = null) + { + $newPath = $this->path.'/'.$name; file_put_contents($newPath, $data); clearstatcache(true, $newPath); - } /** - * Creates a new subdirectory + * Creates a new subdirectory. * * @param string $name - * @return void */ - function createDirectory($name) { - - $newPath = $this->path . '/' . $name; + public function createDirectory($name) + { + $newPath = $this->path.'/'.$name; mkdir($newPath); clearstatcache(true, $newPath); - } /** - * Returns a specific child node, referenced by its name + * 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 self($path); - - } else { - - return new File($path); + public 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 self($path); + } else { + return new File($path); } - } /** - * Returns an array with all the child nodes + * Returns an array with all the child nodes. * * @return DAV\INode[] */ - function getChildren() { - + public function getChildren() + { $nodes = []; $iterator = new \FilesystemIterator( $this->path, @@ -101,51 +99,49 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota { | \FilesystemIterator::SKIP_DOTS ); foreach ($iterator as $entry) { - $nodes[] = $this->getChild($entry->getFilename()); - } - return $nodes; + return $nodes; } /** * Checks if a child exists. * * @param string $name + * * @return bool */ - function childExists($name) { + public function childExists($name) + { + $path = $this->path.'/'.$name; - $path = $this->path . '/' . $name; return file_exists($path); - } /** - * Deletes all files in this directory, and then itself - * - * @return void + * Deletes all files in this directory, and then itself. */ - function delete() { - - foreach ($this->getChildren() as $child) $child->delete(); + public function delete() + { + foreach ($this->getChildren() as $child) { + $child->delete(); + } rmdir($this->path); - } /** - * Returns available diskspace information + * Returns available diskspace information. * * @return array */ - function getQuotaInfo() { + public function getQuotaInfo() + { $absolute = realpath($this->path); + return [ disk_total_space($absolute) - disk_free_space($absolute), - disk_free_space($absolute) + disk_free_space($absolute), ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/File.php index 4fc5af0574b..b78a801389c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/File.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/File.php @@ -1,66 +1,61 @@ path, $data); clearstatcache(true, $this->path); - } /** - * Returns the data + * Returns the data. * * @return resource */ - function get() { - + public function get() + { return fopen($this->path, 'r'); - } /** - * Delete the current file - * - * @return void + * Delete the current file. */ - function delete() { - + public function delete() + { unlink($this->path); - } /** - * Returns the size of the node, in bytes + * Returns the size of the node, in bytes. * * @return int */ - function getSize() { - + public function getSize() + { return filesize($this->path); - } /** - * Returns the ETag for a file + * 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. @@ -69,27 +64,24 @@ class File extends Node implements DAV\IFile { * * @return mixed */ - function getETag() { - - return '"' . sha1( - fileinode($this->path) . - filesize($this->path) . + public function getETag() + { + return '"'.sha1( + fileinode($this->path). + filesize($this->path). filemtime($this->path) - ) . '"'; - + ).'"'; } /** - * Returns the mime-type for a file + * Returns the mime-type for a file. * * If null is returned, we'll assume application/octet-stream * * @return mixed */ - function getContentType() { - + public function getContentType() + { return null; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/Node.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/Node.php index 424718f966e..32aa74755c3 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/Node.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/FS/Node.php @@ -1,12 +1,15 @@ path = $path; - - } - - + protected $overrideName; /** - * Returns the name of the node + * Sets up the node, expects a full path name. + * + * If $overrideName is set, this node shows up in the tree under a + * different name. In this case setName() will be disabled. + * + * @param string $path + * @param string $overrideName + */ + public function __construct($path, $overrideName = null) + { + $this->path = $path; + $this->overrideName = $overrideName; + } + + /** + * Returns the name of the node. * * @return string */ - function getName() { + public function getName() + { + if ($this->overrideName) { + return $this->overrideName; + } + + list(, $name) = Uri\split($this->path); - list(, $name) = URLUtil::splitPath($this->path); return $name; - } /** - * Renames the node + * Renames the node. * * @param string $name The new name - * @return void */ - function setName($name) { + public function setName($name) + { + if ($this->overrideName) { + throw new Forbidden('This node cannot be renamed'); + } - list($parentPath, ) = URLUtil::splitPath($this->path); - list(, $newName) = URLUtil::splitPath($name); + list($parentPath) = Uri\split($this->path); + list(, $newName) = Uri\split($name); - $newPath = $parentPath . '/' . $newName; + $newPath = $parentPath.'/'.$newName; rename($this->path, $newPath); $this->path = $newPath; - } /** - * Returns the last modification time, as a unix timestamp + * Returns the last modification time, as a unix timestamp. * * @return int */ - function getLastModified() { - + public function getLastModified() + { return filemtime($this->path); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/Directory.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/Directory.php index dd5f992dbce..d6aea009492 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/Directory.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/Directory.php @@ -1,21 +1,23 @@ path . '/' . $name; + if ('.' == $name || '..' == $name) { + throw new DAV\Exception\Forbidden('Permission denied to . and ..'); + } + $newPath = $this->path.'/'.$name; file_put_contents($newPath, $data); clearstatcache(true, $newPath); - return '"' . sha1( - fileinode($newPath) . - filesize($newPath) . + return '"'.sha1( + fileinode($newPath). + filesize($newPath). filemtime($newPath) - ) . '"'; - + ).'"'; } /** - * Creates a new subdirectory + * Creates a new subdirectory. * * @param string $name - * @return void */ - function createDirectory($name) { - + public function createDirectory($name) + { // We're not allowing dots - if ($name == '.' || $name == '..') throw new DAV\Exception\Forbidden('Permission denied to . and ..'); - $newPath = $this->path . '/' . $name; + if ('.' == $name || '..' == $name) { + throw new DAV\Exception\Forbidden('Permission denied to . and ..'); + } + $newPath = $this->path.'/'.$name; mkdir($newPath); clearstatcache(true, $newPath); - } /** - * Returns a specific child node, referenced by its name + * 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 self($path); - - } else { - - return new File($path); + public 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 self($path); + } else { + return new File($path); } - } /** * Checks if a child exists. * * @param string $name + * * @return bool */ - function childExists($name) { - - if ($name == '.' || $name == '..') + public function childExists($name) + { + if ('.' == $name || '..' == $name) { throw new DAV\Exception\Forbidden('Permission denied to . and ..'); + } + $path = $this->path.'/'.$name; - $path = $this->path . '/' . $name; return file_exists($path); - } /** - * Returns an array with all the child nodes + * Returns an array with all the child nodes. * * @return DAV\INode[] */ - function getChildren() { - + public function getChildren() + { $nodes = []; $iterator = new \FilesystemIterator( $this->path, @@ -130,44 +135,43 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTa ); foreach ($iterator as $entry) { - $nodes[] = $this->getChild($entry->getFilename()); - } - return $nodes; + return $nodes; } /** - * Deletes all files in this directory, and then itself + * Deletes all files in this directory, and then itself. * * @return bool */ - function delete() { - + public function delete() + { // Deleting all children - foreach ($this->getChildren() as $child) $child->delete(); + foreach ($this->getChildren() as $child) { + $child->delete(); + } // Removing the directory itself rmdir($this->path); return true; - } /** - * Returns available diskspace information + * Returns available diskspace information. * * @return array */ - function getQuotaInfo() { - + public function getQuotaInfo() + { $total = disk_total_space(realpath($this->path)); $free = disk_free_space(realpath($this->path)); return [ $total - $free, - $free + $free, ]; } @@ -186,13 +190,14 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTa * 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 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) { - + public 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 self && !$sourceNode instanceof File) { @@ -200,12 +205,8 @@ class Directory extends Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTa } // PHP allows us to access protected properties from other objects, as - // long as they are defined in a class that has a shared inheritence + // long as they are defined in a class that has a shared inheritance // with the current class. - rename($sourceNode->path, $this->path . '/' . $targetName); - - return true; - + return rename($sourceNode->path, $this->path.'/'.$targetName); } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/File.php index eb5ae19fec5..060ef5a4857 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/File.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/FSExt/File.php @@ -1,33 +1,36 @@ path, $data); clearstatcache(true, $this->path); - return $this->getETag(); + return $this->getETag(); } /** @@ -53,21 +56,22 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport { * time. * * @param resource|string $data - * @param int $rangeType - * @param int $offset + * @param int $rangeType + * @param int $offset + * * @return string|null */ - function patch($data, $rangeType, $offset = null) { - + public function patch($data, $rangeType, $offset = null) + { switch ($rangeType) { - case 1 : + case 1: $f = fopen($this->path, 'a'); break; - case 2 : + case 2: $f = fopen($this->path, 'c'); fseek($f, $offset); break; - case 3 : + case 3: $f = fopen($this->path, 'c'); fseek($f, $offset, SEEK_END); break; @@ -79,34 +83,32 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport { } fclose($f); clearstatcache(true, $this->path); - return $this->getETag(); + return $this->getETag(); } /** - * Returns the data + * Returns the data. * * @return resource */ - function get() { - + public function get() + { return fopen($this->path, 'r'); - } /** - * Delete the current file + * Delete the current file. * * @return bool */ - function delete() { - + public function delete() + { return unlink($this->path); - } /** - * Returns the ETag for a file + * 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. @@ -115,38 +117,34 @@ class File extends Node implements DAV\PartialUpdate\IPatchSupport { * * @return string|null */ - function getETag() { - - return '"' . sha1( - fileinode($this->path) . - filesize($this->path) . + public function getETag() + { + return '"'.sha1( + fileinode($this->path). + filesize($this->path). filemtime($this->path) - ) . '"'; - + ).'"'; } /** - * Returns the mime-type for a file + * Returns the mime-type for a file. * * If null is returned, we'll assume application/octet-stream * * @return string|null */ - function getContentType() { - + public function getContentType() + { return null; - } /** - * Returns the size of the file, in bytes + * Returns the size of the file, in bytes. * * @return int */ - function getSize() { - + public function getSize() + { return filesize($this->path); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/File.php index 5161fbd5153..daf83aa4d87 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/File.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/File.php @@ -1,9 +1,11 @@ locksFile = $locksFile; - } /** - * Returns a list of Sabre\DAV\Locks\LockInfo objects + * 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. @@ -47,47 +48,47 @@ class File extends AbstractBackend { * any locks in the subtree of the uri for locks. * * @param string $uri - * @param bool $returnChildLocks + * @param bool $returnChildLocks + * * @return array */ - function getLocks($uri, $returnChildLocks) { - + public 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) || + (0 != $lock->depth && 0 === strpos($uri, $lock->uri.'/')) || // locks on children - ($returnChildLocks && (strpos($lock->uri, $uri . '/') === 0))) { - + ($returnChildLocks && (0 === strpos($lock->uri, $uri.'/')))) { $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]); + if (time() > $lock->timeout + $lock->created) { + unset($newLocks[$k]); + } } - return $newLocks; + return $newLocks; } /** - * Locks a uri + * Locks a uri. * - * @param string $uri + * @param string $uri * @param LockInfo $lockInfo + * * @return bool */ - function lock($uri, LockInfo $lockInfo) { - + public function lock($uri, LockInfo $lockInfo) + { // We're making the lock timeout 30 minutes $lockInfo->timeout = 1800; $lockInfo->created = time(); @@ -105,32 +106,31 @@ class File extends AbstractBackend { } $locks[] = $lockInfo; $this->putData($locks); - return true; + return true; } /** - * Removes a lock from a uri + * Removes a lock from a uri. * - * @param string $uri + * @param string $uri * @param LockInfo $lockInfo + * * @return bool */ - function unlock($uri, LockInfo $lockInfo) { - + 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 true; } } - return false; + return false; } /** @@ -138,9 +138,11 @@ class File extends AbstractBackend { * * @return array */ - protected function getData() { - - if (!file_exists($this->locksFile)) return []; + protected function getData() + { + if (!file_exists($this->locksFile)) { + return []; + } // opening up the file, and creating a shared lock $handle = fopen($this->locksFile, 'r'); @@ -155,19 +157,20 @@ class File extends AbstractBackend { // Unserializing and checking if the resource file contains data for this file $data = unserialize($data); - if (!$data) return []; - return $data; + if (!$data) { + return []; + } + return $data; } /** - * Saves the lockdata + * Saves the lockdata. * * @param array $newData - * @return void */ - protected function putData(array $newData) { - + protected function putData(array $newData) + { // opening up the file, and creating an exclusive lock $handle = fopen($this->locksFile, 'a+'); flock($handle, LOCK_EX); @@ -179,7 +182,5 @@ class File extends AbstractBackend { fwrite($handle, serialize($newData)); flock($handle, LOCK_UN); fclose($handle); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/Backend/PDO.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/Backend/PDO.php index 510f266f7e6..36a12d1abb6 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/Backend/PDO.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/Backend/PDO.php @@ -1,5 +1,7 @@ pdo = $pdo; - } /** - * Returns a list of Sabre\DAV\Locks\LockInfo objects + * 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. @@ -51,16 +52,17 @@ class PDO extends AbstractBackend { * any locks in the subtree of the uri for locks. * * @param string $uri - * @param bool $returnChildLocks + * @param bool $returnChildLocks + * * @return array */ - function getLocks($uri, $returnChildLocks) { - + public 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)) AND ((uri = ?)'; - $params = [time(),$uri]; + $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE (created > (? - timeout)) AND ((uri = ?)'; + $params = [time(), $uri]; // We need to check locks for every part in the uri. $uriParts = explode('/', $uri); @@ -71,20 +73,18 @@ class PDO extends AbstractBackend { $currentPath = ''; foreach ($uriParts as $part) { - - if ($currentPath) $currentPath .= '/'; + if ($currentPath) { + $currentPath .= '/'; + } $currentPath .= $part; $query .= ' OR (depth!=0 AND uri = ?)'; $params[] = $currentPath; - } if ($returnChildLocks) { - $query .= ' OR (uri LIKE ?)'; - $params[] = $uri . '/%'; - + $params[] = $uri.'/%'; } $query .= ')'; @@ -94,7 +94,6 @@ class PDO extends AbstractBackend { $lockList = []; foreach ($result as $row) { - $lockInfo = new LockInfo(); $lockInfo->owner = $row['owner']; $lockInfo->token = $row['token']; @@ -104,22 +103,21 @@ class PDO extends AbstractBackend { $lockInfo->depth = $row['depth']; $lockInfo->uri = $row['uri']; $lockList[] = $lockInfo; - } return $lockList; - } /** - * Locks a uri + * Locks a uri. * - * @param string $uri + * @param string $uri * @param LockInfo $lockInfo + * * @return bool */ - function lock($uri, LockInfo $lockInfo) { - + public function lock($uri, LockInfo $lockInfo) + { // We're making the lock timeout 30 minutes $lockInfo->timeout = 30 * 60; $lockInfo->created = time(); @@ -128,11 +126,13 @@ class PDO extends AbstractBackend { $locks = $this->getLocks($uri, false); $exists = false; foreach ($locks as $lock) { - if ($lock->token == $lockInfo->token) $exists = true; + 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 = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); $stmt->execute([ $lockInfo->owner, $lockInfo->timeout, @@ -140,10 +140,10 @@ class PDO extends AbstractBackend { $lockInfo->depth, $uri, $lockInfo->created, - $lockInfo->token + $lockInfo->token, ]); } else { - $stmt = $this->pdo->prepare('INSERT INTO ' . $this->tableName . ' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); $stmt->execute([ $lockInfo->owner, $lockInfo->timeout, @@ -151,30 +151,26 @@ class PDO extends AbstractBackend { $lockInfo->depth, $uri, $lockInfo->created, - $lockInfo->token + $lockInfo->token, ]); } return true; - } - - /** - * Removes a lock from a uri + * Removes a lock from a uri. * - * @param string $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 = ?'); + public 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; - + return 1 === $stmt->rowCount(); } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/LockInfo.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/LockInfo.php index 2c8cca0fee4..df82275665d 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/LockInfo.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Locks/LockInfo.php @@ -1,9 +1,11 @@ locksBackend = $locksBackend; - } /** - * Initializes the plugin + * 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) { - + public function initialize(DAV\Server $server) + { $this->server = $server; $this->server->xml->elementMap['{DAV:}lockinfo'] = 'Sabre\\DAV\\Xml\\Request\\Lock'; - $server->on('method:LOCK', [$this, 'httpLock']); - $server->on('method:UNLOCK', [$this, 'httpUnlock']); + $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']); - + $server->on('propFind', [$this, 'propFind']); + $server->on('afterUnbind', [$this, 'afterUnbind']); } /** @@ -77,31 +76,28 @@ class Plugin extends DAV\ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'locks'; - } /** * This method is called after most properties have been found - * it allows us to add in any Lock-related properties + * it allows us to add in any Lock-related properties. * * @param DAV\PropFind $propFind - * @param DAV\INode $node - * @return void + * @param DAV\INode $node */ - function propFind(DAV\PropFind $propFind, DAV\INode $node) { - - $propFind->handle('{DAV:}supportedlock', function() { + public function propFind(DAV\PropFind $propFind, DAV\INode $node) + { + $propFind->handle('{DAV:}supportedlock', function () { return new DAV\Xml\Property\SupportedLock(); }); - $propFind->handle('{DAV:}lockdiscovery', function() use ($propFind) { + $propFind->handle('{DAV:}lockdiscovery', function () use ($propFind) { return new DAV\Xml\Property\LockDiscovery( $this->getLocks($propFind->getPath()) ); }); - } /** @@ -112,12 +108,12 @@ class Plugin extends DAV\ServerPlugin { * available for the specified uri. * * @param string $uri + * * @return array */ - function getHTTPMethods($uri) { - - return ['LOCK','UNLOCK']; - + public function getHTTPMethods($uri) + { + return ['LOCK', 'UNLOCK']; } /** @@ -128,14 +124,13 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getFeatures() { - + public function getFeatures() + { return [2]; - } /** - * Returns all lock information on a particular uri + * 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. * @@ -144,17 +139,17 @@ class Plugin extends DAV\ServerPlugin { * for any possible locks and return those as well. * * @param string $uri - * @param bool $returnChildLocks + * @param bool $returnChildLocks + * * @return array */ - function getLocks($uri, $returnChildLocks = false) { - + public function getLocks($uri, $returnChildLocks = false) + { return $this->locksBackend->getLocks($uri, $returnChildLocks); - } /** - * Locks an uri + * 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 @@ -164,12 +159,13 @@ class Plugin extends DAV\ServerPlugin { * * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpLock(RequestInterface $request, ResponseInterface $response) { - + public function httpLock(RequestInterface $request, ResponseInterface $response) + { $uri = $request->getPath(); $existingLocks = $this->getLocks($uri); @@ -180,7 +176,7 @@ class Plugin extends DAV\ServerPlugin { $existingLock = null; // Checking if there's already non-shared locks on the uri. foreach ($existingLocks as $existingLock) { - if ($existingLock->scope === LockInfo::EXCLUSIVE) { + if (LockInfo::EXCLUSIVE === $existingLock->scope) { throw new DAV\Exception\ConflictingLock($existingLock); } } @@ -188,11 +184,10 @@ class Plugin extends DAV\ServerPlugin { $lockInfo = $this->parseLockRequest($body); $lockInfo->depth = $this->server->getHTTPDepth(); $lockInfo->uri = $uri; - if ($existingLock && $lockInfo->scope != LockInfo::SHARED) + if ($existingLock && LockInfo::SHARED != $lockInfo->scope) { throw new DAV\Exception\ConflictingLock($existingLock); - + } } else { - // Gonna check if this was a lock refresh. $existingLocks = $this->getLocks($uri); $conditions = $this->server->getIfConditions($request); @@ -201,7 +196,7 @@ class Plugin extends DAV\ServerPlugin { foreach ($existingLocks as $existingLock) { foreach ($conditions as $condition) { foreach ($condition['tokens'] as $token) { - if ($token['token'] === 'opaquelocktoken:' . $existingLock->token) { + if ($token['token'] === 'opaquelocktoken:'.$existingLock->token) { $found = $existingLock; break 3; } @@ -216,18 +211,20 @@ class Plugin extends DAV\ServerPlugin { } 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 ($uri != $lockInfo->uri) { + $uri = $lockInfo->uri; + } } - if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout; + if ($timeout = $this->getTimeoutHeader()) { + $lockInfo->timeout = $timeout; + } $newFile = false; @@ -240,56 +237,52 @@ class Plugin extends DAV\ServerPlugin { // // 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->setHeader('Lock-Token', 'token.'>'); $response->setStatus($newFile ? 201 : 200); $response->setBody($this->generateLockResponse($lockInfo)); // Returning false will interrupt the event chain and mark this method // as 'handled'. return false; - } /** - * Unlocks a uri + * 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 RequestInterface $request * @param ResponseInterface $response - * @return void */ - function httpUnlock(RequestInterface $request, ResponseInterface $response) { - + public 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'); - + 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 . '>'; + if ('<' !== $lockToken[0]) { + $lockToken = '<'.$lockToken.'>'; + } foreach ($locks as $lock) { - - if ('token . '>' == $lockToken) { - + if ('token.'>' == $lockToken) { $this->unlockNode($path, $lock); $response->setHeader('Content-Length', '0'); $response->setStatus(204); @@ -297,14 +290,11 @@ class Plugin extends DAV\ServerPlugin { // 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(); - } /** @@ -313,51 +303,54 @@ class Plugin extends DAV\ServerPlugin { * We use this event to clean up any locks that still exist on the node. * * @param string $path - * @return void */ - function afterUnbind($path) { - + public function afterUnbind($path) + { $locks = $this->getLocks($path, $includeChildren = true); foreach ($locks as $lock) { $this->unlockNode($path, $lock); } - } /** - * Locks a uri + * 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 string $uri * @param LockInfo $lockInfo + * * @return bool */ - function lockNode($uri, LockInfo $lockInfo) { + public function lockNode($uri, LockInfo $lockInfo) + { + if (!$this->server->emit('beforeLock', [$uri, $lockInfo])) { + return; + } - if (!$this->server->emit('beforeLock', [$uri, $lockInfo])) return; return $this->locksBackend->lock($uri, $lockInfo); - } /** - * Unlocks a uri + * 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 string $uri * @param LockInfo $lockInfo + * * @return bool */ - function unlockNode($uri, LockInfo $lockInfo) { + public function unlockNode($uri, LockInfo $lockInfo) + { + if (!$this->server->emit('beforeUnlock', [$uri, $lockInfo])) { + return; + } - if (!$this->server->emit('beforeUnlock', [$uri, $lockInfo])) return; return $this->locksBackend->unlock($uri, $lockInfo); - } - /** * Returns the contents of the HTTP Timeout header. * @@ -365,37 +358,36 @@ class Plugin extends DAV\ServerPlugin { * * @return int */ - function getTimeoutHeader() { - + public function getTimeoutHeader() + { $header = $this->server->httpRequest->getHeader('Timeout'); if ($header) { - - if (stripos($header, 'second-') === 0) $header = (int)(substr($header, 7)); - elseif (stripos($header, 'infinite') === 0) $header = LockInfo::TIMEOUT_INFINITE; - else throw new DAV\Exception\BadRequest('Invalid HTTP timeout header'); - + if (0 === stripos($header, 'second-')) { + $header = (int) (substr($header, 7)); + } elseif (0 === stripos($header, 'infinite')) { + $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 + * Generates the response for successful LOCK requests. * * @param LockInfo $lockInfo + * * @return string */ - protected function generateLockResponse(LockInfo $lockInfo) { - + protected function generateLockResponse(LockInfo $lockInfo) + { return $this->server->xml->write('{DAV:}prop', [ - '{DAV:}lockdiscovery' => - new DAV\Xml\Property\LockDiscovery([$lockInfo]) + '{DAV:}lockdiscovery' => new DAV\Xml\Property\LockDiscovery([$lockInfo]), ]); } @@ -410,11 +402,10 @@ class Plugin extends DAV\ServerPlugin { * tokens. * * @param RequestInterface $request - * @param mixed $conditions - * @return void + * @param mixed $conditions */ - function validateTokens(RequestInterface $request, &$conditions) { - + public function validateTokens(RequestInterface $request, &$conditions) + { // First we need to gather a list of locks that must be satisfied. $mustLocks = []; $method = $request->getMethod(); @@ -422,24 +413,23 @@ class Plugin extends DAV\ServerPlugin { // 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' : + case 'DELETE': $mustLocks = array_merge($mustLocks, $this->getLocks( $request->getPath(), true )); break; - case 'MKCOL' : - case 'MKCALENDAR' : - case 'PROPPATCH' : - case 'PUT' : - case 'PATCH' : + case 'MKCOL': + case 'MKCALENDAR': + case 'PROPPATCH': + case 'PUT': + case 'PATCH': $mustLocks = array_merge($mustLocks, $this->getLocks( $request->getPath(), false )); break; - case 'MOVE' : + case 'MOVE': $mustLocks = array_merge($mustLocks, $this->getLocks( $request->getPath(), true @@ -449,13 +439,13 @@ class Plugin extends DAV\ServerPlugin { false )); break; - case 'COPY' : + case 'COPY': $mustLocks = array_merge($mustLocks, $this->getLocks( $this->server->calculateUri($request->getHeader('Destination')), false )); break; - case 'LOCK' : + case 'LOCK': //Temporary measure.. figure out later why this is needed // Here we basically ignore all incoming tokens... foreach ($conditions as $ii => $condition) { @@ -463,31 +453,29 @@ class Plugin extends DAV\ServerPlugin { $conditions[$ii]['tokens'][$jj]['validToken'] = true; } } - return; + 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; + 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:') { + if ('opaquelocktoken:' !== substr($token['token'], 0, 16)) { 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]); @@ -497,9 +485,7 @@ class Plugin extends DAV\ServerPlugin { // Advancing to the next token continue 2; - } - } // If we got here, it means that there was a @@ -514,42 +500,34 @@ class Plugin extends DAV\ServerPlugin { // 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 + * Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object. * * @param string $body + * * @return LockInfo */ - protected function parseLockRequest($body) { - + protected function parseLockRequest($body) + { $result = $this->server->xml->expect( '{DAV:}lockinfo', $body @@ -562,7 +540,6 @@ class Plugin extends DAV\ServerPlugin { $lockInfo->scope = $result->scope; return $lockInfo; - } /** @@ -576,14 +553,12 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'The locks plugin turns this server into a class-2 WebDAV server and adds support for LOCK and UNLOCK', - 'link' => 'http://sabre.io/dav/locks/', + 'link' => 'http://sabre.io/dav/locks/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/MkCol.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/MkCol.php index 042e14bca31..f3c5ea5c033 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/MkCol.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/MkCol.php @@ -1,5 +1,7 @@ resourceType = $resourceType; parent::__construct($mutations); - } /** @@ -47,10 +48,9 @@ class MkCol extends PropPatch { * * @return string[] */ - function getResourceType() { - + public function getResourceType() + { return $this->resourceType; - } /** @@ -61,12 +61,11 @@ class MkCol extends PropPatch { * checked. * * @param string|string[] $resourceType + * * @return bool */ - function hasResourceType($resourceType) { - - return count(array_diff((array)$resourceType, $this->resourceType)) === 0; - + public function hasResourceType($resourceType) + { + return 0 === count(array_diff((array) $resourceType, $this->resourceType)); } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Mount/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Mount/Plugin.php index dc923ad8522..5eaa4d4629c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Mount/Plugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Mount/Plugin.php @@ -1,5 +1,7 @@ server = $server; $this->server->on('method:GET', [$this, 'httpGet'], 90); - } /** * 'beforeMethod' event handles. This event handles intercepts GET requests ending - * with ?mount + * with ?mount. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpGet(RequestInterface $request, ResponseInterface $response) { - + public function httpGet(RequestInterface $request, ResponseInterface $response) + { $queryParams = $request->getQueryParameters(); - if (!array_key_exists('mount', $queryParams)) return; + if (!array_key_exists('mount', $queryParams)) { + return; + } $currentUri = $request->getAbsoluteUrl(); @@ -59,28 +62,23 @@ class Plugin extends DAV\ServerPlugin { // Returning false to break the event chain return false; - } /** - * Generates the davmount response + * Generates the davmount response. * * @param ResponseInterface $response - * @param string $uri absolute uri - * @return void + * @param string $uri absolute uri */ - function davMount(ResponseInterface $response, $uri) { - + public 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 ""; + echo ' ', htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "\n"; + echo ''; $response->setBody(ob_get_clean()); - } - - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Node.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Node.php index ef6eea18e06..948060d9a0c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Node.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Node.php @@ -1,9 +1,11 @@ server = $server; $server->on('method:PATCH', [$this, 'httpPatch']); - } /** @@ -56,10 +56,9 @@ class Plugin extends DAV\ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'partialupdate'; - } /** @@ -74,10 +73,11 @@ class Plugin extends DAV\ServerPlugin { * - the node implements our partial update interface * * @param string $uri + * * @return array */ - function getHTTPMethods($uri) { - + public function getHTTPMethods($uri) + { $tree = $this->server->tree; if ($tree->nodeExists($uri)) { @@ -86,8 +86,8 @@ class Plugin extends DAV\ServerPlugin { return ['PATCH']; } } - return []; + return []; } /** @@ -95,25 +95,23 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getFeatures() { - + public function getFeatures() + { return ['sabredav-partialupdate']; - } /** - * Patch an uri + * 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 RequestInterface $request * @param ResponseInterface $response - * @return void */ - function httpPatch(RequestInterface $request, ResponseInterface $response) { - + public function httpPatch(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); // Get the node. Will throw a 404 if not found @@ -129,53 +127,55 @@ class Plugin extends DAV\ServerPlugin { } $contentType = strtolower( - $request->getHeader('Content-Type') + (string) $request->getHeader('Content-Type') ); - if ($contentType != 'application/x-sabredav-partialupdate') { - throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); + if ('application/x-sabredav-partialupdate' != $contentType) { + 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'); - + if (!$len) { + throw new DAV\Exception\LengthRequired('A Content-Length header is required'); + } switch ($range[0]) { - case self::RANGE_START : + 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] . ')'); + 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'); + 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])) + if (!$this->server->emit('beforeWriteContent', [$path, $node, null])) { return; + } $body = $this->server->httpRequest->getBody(); - $etag = $node->patch($body, $range[0], isset($range[1]) ? $range[1] : null); $this->server->emit('afterWriteContent', [$path, $node]); $response->setHeader('Content-Length', '0'); - if ($etag) $response->setHeader('ETag', $etag); + if ($etag) { + $response->setHeader('ETag', $etag); + } $response->setStatus(204); // Breaks the event chain return false; - } /** - * Returns the HTTP custom range update header + * 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, @@ -192,24 +192,28 @@ class Plugin extends DAV\ServerPlugin { * [3,-5] - update from 5 bytes from the end of the file. * * @param RequestInterface $request + * * @return array|null */ - function getHTTPUpdateRange(RequestInterface $request) { - + public function getHTTPUpdateRange(RequestInterface $request) + { $range = $request->getHeader('X-Update-Range'); - if (is_null($range)) return null; + 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]; - } else { - return [self::RANGE_END, $matches[4]]; + if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i', $range, $matches)) { + return null; } + if ('append' === $matches[1]) { + return [self::RANGE_APPEND]; + } elseif (strlen($matches[2]) > 0) { + return [self::RANGE_START, (int) $matches[2], (int) $matches[3] ?: null]; + } else { + return [self::RANGE_END, (int) $matches[4]]; + } } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropFind.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropFind.php index 0940a1ce29a..4b6fe28ebf7 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropFind.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropFind.php @@ -1,5 +1,7 @@ path = $path; $this->properties = $properties; $this->depth = $depth; $this->requestType = $requestType; - if ($requestType === self::ALLPROPS) { + if (self::ALLPROPS === $requestType) { $this->properties = [ '{DAV:}getlastmodified', '{DAV:}getcontentlength', @@ -60,13 +62,10 @@ class PropFind { } foreach ($this->properties as $propertyName) { - // Seeding properties with 404's. $this->result[$propertyName] = [404, null]; - } $this->itemsLeft = count($this->result); - } /** @@ -88,68 +87,65 @@ class PropFind { * It's also possible to not pass a callback, but immediately pass a value * * @param string $propertyName - * @param mixed $valueOrCallBack - * @return void + * @param mixed $valueOrCallBack */ - function handle($propertyName, $valueOrCallBack) { - - if ($this->itemsLeft && isset($this->result[$propertyName]) && $this->result[$propertyName][0] === 404) { + public function handle($propertyName, $valueOrCallBack) + { + if ($this->itemsLeft && isset($this->result[$propertyName]) && 404 === $this->result[$propertyName][0]) { if (is_callable($valueOrCallBack)) { $value = $valueOrCallBack(); } else { $value = $valueOrCallBack; } if (!is_null($value)) { - $this->itemsLeft--; + --$this->itemsLeft; $this->result[$propertyName] = [200, $value]; } } - } /** - * Sets the value of the property + * 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 + * @param mixed $value + * @param int $status */ - function set($propertyName, $value, $status = null) { - + public 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) { + if (self::ALLPROPS === $this->requestType) { $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++; + if (404 !== $status && 404 === $this->result[$propertyName][0]) { + --$this->itemsLeft; + } elseif (404 === $status && 404 !== $this->result[$propertyName][0]) { + ++$this->itemsLeft; } $this->result[$propertyName] = [$status, $value]; - } /** * Returns the current value for a property. * * @param string $propertyName + * * @return mixed */ - function get($propertyName) { - + public function get($propertyName) + { return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null; - } /** @@ -159,24 +155,22 @@ class PropFind { * null will be returned. * * @param string $propertyName + * * @return int|null */ - function getStatus($propertyName) { - + public 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) { - + public function setPath($path) + { $this->path = $path; - } /** @@ -184,10 +178,9 @@ class PropFind { * * @return string */ - function getPath() { - + public function getPath() + { return $this->path; - } /** @@ -195,22 +188,19 @@ class PropFind { * * @return int */ - function getDepth() { - + public function getDepth() + { return $this->depth; - } /** * Updates the depth of this propfind request. * * @param int $depth - * @return void */ - function setDepth($depth) { - + public function setDepth($depth) + { $this->depth = $depth; - } /** @@ -219,19 +209,19 @@ class PropFind { * * @return array */ - function get404Properties() { - - if ($this->itemsLeft === 0) { + public function get404Properties() + { + if (0 === $this->itemsLeft) { return []; } $result = []; foreach ($this->result as $propertyName => $stuff) { - if ($stuff[0] === 404) { + if (404 === $stuff[0]) { $result[] = $propertyName; } } - return $result; + return $result; } /** @@ -241,10 +231,9 @@ class PropFind { * * @return array */ - function getRequestedProperties() { - + public function getRequestedProperties() + { return $this->properties; - } /** @@ -252,10 +241,9 @@ class PropFind { * * @return bool */ - function isAllProps() { - - return $this->requestType === self::ALLPROPS; - + public function isAllProps() + { + return self::ALLPROPS === $this->requestType; } /** @@ -270,8 +258,8 @@ class PropFind { * * @return array */ - function getResultForMultiStatus() { - + public function getResultForMultiStatus() + { $r = [ 200 => [], 404 => [], @@ -284,9 +272,11 @@ class PropFind { } } // Removing the 404's for multi-status requests. - if ($this->requestType === self::ALLPROPS) unset($r[404]); - return $r; + if (self::ALLPROPS === $this->requestType) { + unset($r[404]); + } + return $r; } /** @@ -307,12 +297,12 @@ class PropFind { protected $depth = 0; /** - * The type of request. See the TYPE constants + * The type of request. See the TYPE constants. */ protected $requestType; /** - * A list of requested properties + * A list of requested properties. * * @var array */ @@ -343,5 +333,4 @@ class PropFind { * @var int */ protected $itemsLeft; - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropPatch.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropPatch.php index 6d599dacc4a..160bdb1a886 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropPatch.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropPatch.php @@ -1,5 +1,7 @@ mutations = $mutations; - } /** @@ -81,21 +82,17 @@ class PropPatch { * code associated with the operation. * * @param string|string[] $properties - * @param callable $callback - * @return void + * @param callable $callback */ - function handle($properties, callable $callback) { - + public function handle($properties, callable $callback) + { $usedProperties = []; - foreach ((array)$properties as $propertyName) { - + 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. @@ -107,9 +104,8 @@ class PropPatch { // 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 + $callback, ]; - } /** @@ -118,10 +114,9 @@ class PropPatch { * this that you promise to process _all_ properties that are coming in. * * @param callable $callback - * @return void */ - function handleRemaining(callable $callback) { - + public function handleRemaining(callable $callback) + { $properties = $this->getRemainingMutations(); if (!$properties) { // Nothing to do, don't register callback @@ -134,44 +129,39 @@ class PropPatch { $this->propertyUpdateCallbacks[] = [ $properties, - $callback + $callback, ]; } - } /** * Sets the result code for one or more properties. * * @param string|string[] $properties - * @param int $resultCode - * @return void + * @param int $resultCode */ - function setResultCode($properties, $resultCode) { - - foreach ((array)$properties as $propertyName) { + public 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) { - + public function setRemainingResultCode($resultCode) + { $this->setResultCode( $this->getRemainingMutations(), $resultCode ); - } /** @@ -181,8 +171,8 @@ class PropPatch { * * @return string[] */ - function getRemainingMutations() { - + public function getRemainingMutations() + { $remaining = []; foreach ($this->mutations as $propertyName => $propValue) { if (!isset($this->result[$propertyName])) { @@ -191,7 +181,6 @@ class PropPatch { } return $remaining; - } /** @@ -201,8 +190,8 @@ class PropPatch { * * @return array */ - function getRemainingValues() { - + public function getRemainingValues() + { $remaining = []; foreach ($this->mutations as $propertyName => $propValue) { if (!isset($this->result[$propertyName])) { @@ -211,7 +200,6 @@ class PropPatch { } return $remaining; - } /** @@ -222,20 +210,17 @@ class PropPatch { * * @return bool */ - function commit() { - + public 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; } @@ -244,37 +229,32 @@ class PropPatch { } 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) { + if (202 === $status) { // Failed dependency $this->result[$propertyName] = 424; } } - } return !$this->failed; - } /** * Executes a property callback with the single-property syntax. * - * @param string $propertyName + * @param string $propertyName * @param callable $callback - * @return void */ - private function doCallBackSingleProp($propertyName, callable $callback) { - + private function doCallBackSingleProp($propertyName, callable $callback) + { $result = $callback($this->mutations[$propertyName]); if (is_bool($result)) { if ($result) { @@ -297,18 +277,16 @@ class PropPatch { if ($result >= 400) { $this->failed = true; } - } /** * Executes a property callback with the multi-property syntax. * - * @param array $propertyList + * @param array $propertyList * @param callable $callback - * @return void */ - private function doCallBackMultiProp(array $propertyList, callable $callback) { - + private function doCallBackMultiProp(array $propertyList, callable $callback) + { $argument = []; foreach ($propertyList as $propertyName) { $argument[$propertyName] = $this->mutations[$propertyName]; @@ -327,16 +305,13 @@ class PropPatch { $this->failed = true; } $this->result[$propertyName] = $resultCode; - } - } elseif ($result === true) { - + } elseif (true === $result) { // Success foreach ($argument as $propertyName => $propertyValue) { $this->result[$propertyName] = is_null($propertyValue) ? 204 : 200; } - - } elseif ($result === false) { + } elseif (false === $result) { // Fail :( $this->failed = true; foreach ($propertyList as $propertyName) { @@ -345,7 +320,6 @@ class PropPatch { } else { throw new UnexpectedValueException('A callback sent to handle() did not return an array or a bool'); } - } /** @@ -353,21 +327,18 @@ class PropPatch { * * @return array */ - function getResult() { - + public function getResult() + { return $this->result; - } /** - * Returns the full list of mutations + * Returns the full list of mutations. * * @return array */ - function getMutations() { - + public function getMutations() + { return $this->mutations; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php index b15d7fef944..4bdc447758c 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php @@ -1,5 +1,7 @@ isAllProps(). * - * @param string $path + * @param string $path * @param PropFind $propFind - * @return void */ - function propFind($path, PropFind $propFind); + public function propFind($path, PropFind $propFind); /** - * Updates properties for a path + * Updates properties for a path. * * This method received a PropPatch object, which contains all the * information about the update. @@ -45,11 +46,10 @@ interface BackendInterface { * 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 string $path * @param PropPatch $propPatch - * @return void */ - function propPatch($path, PropPatch $propPatch); + public function propPatch($path, PropPatch $propPatch); /** * This method is called after a node is deleted. @@ -60,12 +60,11 @@ interface BackendInterface { * tree. * * @param string $path - * @return void */ - function delete($path); + public function delete($path); /** - * This method is called after a successful MOVE + * 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 @@ -73,8 +72,6 @@ interface BackendInterface { * * @param string $source * @param string $destination - * @return void */ - function move($source, $destination); - + public function move($source, $destination); } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php index 6f3f1feaf57..e640f420ef1 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php @@ -1,5 +1,7 @@ pdo = $pdo; - } /** @@ -73,42 +74,40 @@ class PDO implements BackendInterface { * However, you can also support the 'allprops' property here. In that * case, you should check for $propFind->isAllProps(). * - * @param string $path + * @param string $path * @param PropFind $propFind - * @return void */ - function propFind($path, PropFind $propFind) { - - if (!$propFind->isAllProps() && count($propFind->get404Properties()) === 0) { + public function propFind($path, PropFind $propFind) + { + if (!$propFind->isAllProps() && 0 === count($propFind->get404Properties())) { return; } - $query = 'SELECT name, value, valuetype FROM ' . $this->tableName . ' WHERE path = ?'; + $query = 'SELECT name, value, valuetype FROM '.$this->tableName.' WHERE path = ?'; $stmt = $this->pdo->prepare($query); $stmt->execute([$path]); while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { - if (gettype($row['value']) === 'resource') { + if ('resource' === gettype($row['value'])) { $row['value'] = stream_get_contents($row['value']); } switch ($row['valuetype']) { - case null : - case self::VT_STRING : + case null: + case self::VT_STRING: $propFind->set($row['name'], $row['value']); break; - case self::VT_XML : + case self::VT_XML: $propFind->set($row['name'], new Complex($row['value'])); break; - case self::VT_OBJECT : + case self::VT_OBJECT: $propFind->set($row['name'], unserialize($row['value'])); break; } } - } /** - * Updates properties for a path + * Updates properties for a path. * * This method received a PropPatch object, which contains all the * information about the update. @@ -116,38 +115,30 @@ class PDO implements BackendInterface { * 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 string $path * @param PropPatch $propPatch - * @return void */ - function propPatch($path, PropPatch $propPatch) { - - $propPatch->handleRemaining(function($properties) use ($path) { - - - if ($this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'pgsql') { - + public function propPatch($path, PropPatch $propPatch) + { + $propPatch->handleRemaining(function ($properties) use ($path) { + if ('pgsql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) { $updateSql = <<tableName} (path, name, valuetype, value) VALUES (:path, :name, :valuetype, :value) ON CONFLICT (path, name) DO UPDATE SET valuetype = :valuetype, value = :value SQL; - - } else { $updateSql = <<tableName} (path, name, valuetype, value) VALUES (:path, :name, :valuetype, :value) SQL; - } $updateStmt = $this->pdo->prepare($updateSql); - $deleteStmt = $this->pdo->prepare("DELETE FROM " . $this->tableName . " WHERE path = ? AND name = ?"); + $deleteStmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE path = ? AND name = ?'); foreach ($properties as $name => $value) { - if (!is_null($value)) { if (is_scalar($value)) { $valueType = self::VT_STRING; @@ -165,17 +156,13 @@ SQL; $updateStmt->bindParam('value', $value, \PDO::PARAM_LOB); $updateStmt->execute(); - } else { $deleteStmt->execute([$path, $name]); } - } return true; - }); - } /** @@ -187,26 +174,24 @@ SQL; * tree. * * @param string $path - * @return void */ - function delete($path) { - - $stmt = $this->pdo->prepare("DELETE FROM " . $this->tableName . " WHERE path = ? OR path LIKE ? ESCAPE '='"); + public function delete($path) + { + $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName." WHERE path = ? OR path LIKE ? ESCAPE '='"); $childPath = strtr( $path, [ '=' => '==', '%' => '=%', - '_' => '=_' + '_' => '=_', ] - ) . '/%'; + ).'/%'; $stmt->execute([$path, $childPath]); - } /** - * This method is called after a successful MOVE + * 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 @@ -214,33 +199,30 @@ SQL; * * @param string $source * @param string $destination - * @return void */ - function move($source, $destination) { - + public 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 ' . $this->tableName . ' WHERE path = ? OR path LIKE ?'); - $select->execute([$source, $source . '/%']); + $select = $this->pdo->prepare('SELECT id, path FROM '.$this->tableName.' WHERE path = ? OR path LIKE ?'); + $select->execute([$source, $source.'/%']); - $update = $this->pdo->prepare('UPDATE ' . $this->tableName . ' SET path = ? WHERE id = ?'); + $update = $this->pdo->prepare('UPDATE '.$this->tableName.' 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; + if ($row['path'] !== $source && 0 !== strpos($row['path'], $source.'/')) { + continue; + } $trailingPart = substr($row['path'], strlen($source) + 1); $newPath = $destination; if ($trailingPart) { - $newPath .= '/' . $trailingPart; + $newPath .= '/'.$trailingPart; } $update->execute([$newPath, $row['id']]); - } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Plugin.php index a66a14113fb..aa8610eb824 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Plugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Plugin.php @@ -1,5 +1,7 @@ backend = $backend; - } /** @@ -62,15 +63,13 @@ class Plugin extends ServerPlugin { * 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']); + public 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']); - } /** @@ -80,34 +79,34 @@ class Plugin extends ServerPlugin { * plugin will look in the property storage backend to find them. * * @param PropFind $propFind - * @param INode $node - * @return void + * @param INode $node */ - function propFind(PropFind $propFind, INode $node) { - + public function propFind(PropFind $propFind, INode $node) + { $path = $propFind->getPath(); $pathFilter = $this->pathFilter; - if ($pathFilter && !$pathFilter($path)) return; + if ($pathFilter && !$pathFilter($path)) { + return; + } $this->backend->propFind($propFind->getPath(), $propFind); - } /** - * Called during PROPPATCH operations + * 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 string $path * @param PropPatch $propPatch - * @return void */ - function propPatch($path, PropPatch $propPatch) { - + public function propPatch($path, PropPatch $propPatch) + { $pathFilter = $this->pathFilter; - if ($pathFilter && !$pathFilter($path)) return; + if ($pathFilter && !$pathFilter($path)) { + return; + } $this->backend->propPatch($path, $propPatch); - } /** @@ -117,14 +116,14 @@ class Plugin extends ServerPlugin { * database. * * @param string $path - * @return void */ - function afterUnbind($path) { - + public function afterUnbind($path) + { $pathFilter = $this->pathFilter; - if ($pathFilter && !$pathFilter($path)) return; + if ($pathFilter && !$pathFilter($path)) { + return; + } $this->backend->delete($path); - } /** @@ -134,18 +133,20 @@ class Plugin extends ServerPlugin { * * @param string $source * @param string $destination - * @return void */ - function afterMove($source, $destination) { - + public function afterMove($source, $destination) + { $pathFilter = $this->pathFilter; - if ($pathFilter && !$pathFilter($source)) return; + if ($pathFilter && !$pathFilter($source)) { + return; + } // If the destination is filtered, afterUnbind will handle cleaning up // the properties. - if ($pathFilter && !$pathFilter($destination)) return; + if ($pathFilter && !$pathFilter($destination)) { + return; + } $this->backend->move($source, $destination); - } /** @@ -156,10 +157,9 @@ class Plugin extends ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'property-storage'; - } /** @@ -173,13 +173,12 @@ class Plugin extends ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.', - 'link' => 'http://sabre.io/dav/property-storage/', + 'link' => 'http://sabre.io/dav/property-storage/', ]; - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Server.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Server.php index 6805ec0b013..09760e9d111 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Server.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Server.php @@ -1,76 +1,79 @@ 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); @@ -229,18 +220,14 @@ class Server extends EventEmitter implements LoggerAwareInterface { $this->httpResponse = new HTTP\Response(); $this->httpRequest = $this->sapi->getRequest(); $this->addPlugin(new CorePlugin()); - } /** - * Starts the DAV Server - * - * @return void + * Starts the DAV Server. */ - function exec() { - + public function start() + { 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. @@ -252,9 +239,7 @@ class Server extends EventEmitter implements LoggerAwareInterface { // Setting the base url $this->httpRequest->setBaseUrl($this->getBaseUri()); $this->invokeMethod($this->httpRequest, $this->httpResponse); - - } catch (\Exception $e) { - + } catch (\Throwable $e) { try { $this->emit('exception', [$e]); } catch (\Exception $ignore) { @@ -266,10 +251,8 @@ class Server extends EventEmitter implements LoggerAwareInterface { $error->setAttribute('xmlns:s', self::NS_SABREDAV); $DOM->appendChild($error); - $h = function($v) { - - return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8'); - + $h = function ($v) { + return htmlspecialchars((string) $v, ENT_NOQUOTES, 'UTF-8'); }; if (self::$exposeVersion) { @@ -299,18 +282,13 @@ class Server extends EventEmitter implements LoggerAwareInterface { } } - 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'; @@ -318,37 +296,46 @@ class Server extends EventEmitter implements LoggerAwareInterface { $this->httpResponse->setHeaders($headers); $this->httpResponse->setBody($DOM->saveXML()); $this->sapi->sendResponse($this->httpResponse); - } - } /** - * Sets the base server uri + * Alias of start(). + * + * @deprecated + */ + public function exec() + { + $this->start(); + } + + /** + * Sets the base server uri. * * @param string $uri - * @return void */ - function setBaseUri($uri) { - + public function setBaseUri($uri) + { // If the baseUri does not end with a slash, we must add it - if ($uri[strlen($uri) - 1] !== '/') + if ('/' !== $uri[strlen($uri) - 1]) { $uri .= '/'; + } $this->baseUri = $uri; - } /** - * Returns the base responding uri + * Returns the base responding uri. * * @return string */ - function getBaseUri() { + public function getBaseUri() + { + if (is_null($this->baseUri)) { + $this->baseUri = $this->guessBaseUri(); + } - if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri(); return $this->baseUri; - } /** @@ -359,53 +346,50 @@ class Server extends EventEmitter implements LoggerAwareInterface { * * @return string */ - function guessBaseUri() { - + public 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, '?')) + 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); + $decodedUri = HTTP\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, '/') . '/'; + + 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.'); - + 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 + * 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) { - + public function addPlugin(ServerPlugin $plugin) + { $this->plugins[$plugin->getPluginName()] = $plugin; $plugin->initialize($this); - } /** @@ -414,26 +398,26 @@ class Server extends EventEmitter implements LoggerAwareInterface { * This function returns null if the plugin was not found. * * @param string $name + * * @return ServerPlugin */ - function getPlugin($name) { - - if (isset($this->plugins[$name])) + public function getPlugin($name) + { + if (isset($this->plugins[$name])) { return $this->plugins[$name]; + } return null; - } /** - * Returns all plugins + * Returns all plugins. * * @return array */ - function getPlugins() { - + public function getPlugins() + { return $this->plugins; - } /** @@ -441,29 +425,29 @@ class Server extends EventEmitter implements LoggerAwareInterface { * * @return LoggerInterface */ - function getLogger() { - + public function getLogger() + { if (!$this->logger) { $this->logger = new NullLogger(); } - return $this->logger; + return $this->logger; } /** - * Handles a http request, and execute a method based on its name + * Handles a http request, and execute a method based on its name. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @param bool $sendResponse Whether to send the HTTP response to the DAV client. - * @return void + * @param bool $sendResponse whether to send the HTTP response to the DAV client */ - function invokeMethod(RequestInterface $request, ResponseInterface $response, $sendResponse = true) { - + public function invokeMethod(RequestInterface $request, ResponseInterface $response, $sendResponse = true) + { $method = $request->getMethod(); - if (!$this->emit('beforeMethod:' . $method, [$request, $response])) return; - if (!$this->emit('beforeMethod', [$request, $response])) return; + if (!$this->emit('beforeMethod:'.$method, [$request, $response])) { + return; + } if (self::$exposeVersion) { $response->setHeader('X-Sabre-Version', Version::VERSION); @@ -473,32 +457,31 @@ class Server extends EventEmitter implements LoggerAwareInterface { if (!$this->checkPreconditions($request, $response)) { $this->sapi->sendResponse($response); + return; } - if ($this->emit('method:' . $method, [$request, $response])) { - if ($this->emit('method', [$request, $response])) { - $exMessage = "There was no plugin in the system that was willing to handle this " . $method . " method."; - if ($method === "GET") { - $exMessage .= " Enable the Browser plugin to get a better result here."; - } - - // Unsupported method - throw new Exception\NotImplemented($exMessage); + if ($this->emit('method:'.$method, [$request, $response])) { + $exMessage = 'There was no plugin in the system that was willing to handle this '.$method.' method.'; + if ('GET' === $method) { + $exMessage .= ' Enable the Browser plugin to get a better result here.'; } + + // Unsupported method + throw new Exception\NotImplemented($exMessage); } - if (!$this->emit('afterMethod:' . $method, [$request, $response])) return; - if (!$this->emit('afterMethod', [$request, $response])) return; + if (!$this->emit('afterMethod:'.$method, [$request, $response])) { + return; + } - if ($response->getStatus() === null) { + if (null === $response->getStatus()) { throw new Exception('No subsystem set a valid HTTP status code. Something must have interrupted the request without providing further detail.'); } if ($sendResponse) { $this->sapi->sendResponse($response); $this->emit('afterResponse', [$request, $response]); } - } // {{{ HTTP/WebDAV protocol helpers @@ -507,10 +490,11 @@ class Server extends EventEmitter implements LoggerAwareInterface { * Returns an array with all the supported HTTP methods for a specific uri. * * @param string $path + * * @return array */ - function getAllowedMethods($path) { - + public function getAllowedMethods($path) + { $methods = [ 'OPTIONS', 'GET', @@ -521,7 +505,7 @@ class Server extends EventEmitter implements LoggerAwareInterface { 'PROPPATCH', 'COPY', 'MOVE', - 'REPORT' + 'REPORT', ]; // The MKCOL is only allowed on an unmapped uri @@ -532,22 +516,22 @@ class Server extends EventEmitter implements LoggerAwareInterface { } // 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)); + 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 + * Gets the uri for the request, keeping the base uri into consideration. * * @return string */ - function getRequestUri() { - + public function getRequestUri() + { return $this->calculateUri($this->httpRequest->getUrl()); - } /** @@ -559,66 +543,65 @@ class Server extends EventEmitter implements LoggerAwareInterface { * * uri-decodes the path * * @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, '://')) { - + public function calculateUri($uri) + { + if ('' != $uri && '/' != $uri[0] && strpos($uri, '://')) { $uri = parse_url($uri, PHP_URL_PATH); - } - $uri = Uri\normalize(str_replace('//', '/', $uri)); + $uri = Uri\normalize(preg_replace('|/+|', '/', $uri)); $baseUri = Uri\normalize($this->getBaseUri()); - if (strpos($uri, $baseUri) === 0) { - - return trim(URLUtil::decodePath(substr($uri, strlen($baseUri))), '/'); + if (0 === strpos($uri, $baseUri)) { + return trim(HTTP\decodePath(substr($uri, strlen($baseUri))), '/'); // A special case, if the baseUri was accessed without a trailing // slash, we'll accept it as well. - } elseif ($uri . '/' === $baseUri) { - + } elseif ($uri.'/' === $baseUri) { return ''; - } else { - - throw new Exception\Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')'); - + throw new Exception\Forbidden('Requested uri ('.$uri.') is out of base uri ('.$this->getBaseUri().')'); } - } /** - * Returns the HTTP depth header + * 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) { - + public 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 (is_null($depth)) { + return $default; + } + if ('infinity' == $depth) { + return self::DEPTH_INFINITY; + } // If its an unknown value. we'll grab the default - if (!ctype_digit($depth)) return $default; - - return (int)$depth; + if (!ctype_digit($depth)) { + return $default; + } + return (int) $depth; } /** - * Returns the HTTP range header + * Returns the HTTP range header. * * This method returns null if there is no well-formed HTTP range request * header or array($start, $end). @@ -629,24 +612,29 @@ class Server extends EventEmitter implements LoggerAwareInterface { * 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 + * @return int[]|null */ - function getHTTPRange() { - + public function getHTTPRange() + { $range = $this->httpRequest->getHeader('range'); - if (is_null($range)) return null; + 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 (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i', $range, $matches)) { + return null; + } - if ($matches[1] === '' && $matches[2] === '') return null; + if ('' === $matches[1] && '' === $matches[2]) { + return null; + } return [ - $matches[1] !== '' ? $matches[1] : null, - $matches[2] !== '' ? $matches[2] : null, + '' !== $matches[1] ? (int) $matches[1] : null, + '' !== $matches[2] ? (int) $matches[2] : null, ]; - } /** @@ -675,8 +663,8 @@ class Server extends EventEmitter implements LoggerAwareInterface { * * @return array */ - function getHTTPPrefer() { - + public function getHTTPPrefer() + { $result = [ // can be true or false 'respond-async' => false, @@ -689,23 +677,19 @@ class Server extends EventEmitter implements LoggerAwareInterface { ]; if ($prefer = $this->httpRequest->getHeader('Prefer')) { - $result = array_merge( $result, HTTP\parsePrefer($prefer) ); - - } elseif ($this->httpRequest->getHeader('Brief') == 't') { + } elseif ('t' == $this->httpRequest->getHeader('Brief')) { $result['return'] = 'minimal'; } return $result; - } - /** - * Returns information about Copy and Move requests + * 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 @@ -715,74 +699,82 @@ class Server extends EventEmitter implements LoggerAwareInterface { * * 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\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. + * 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) { - + public function getCopyAndMoveInfo(RequestInterface $request) + { // Collecting the relevant HTTP headers - if (!$request->getHeader('Destination')) throw new Exception\BadRequest('The destination header was not supplied'); + 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; + if (!$overwrite) { + $overwrite = 'T'; + } + if ('T' == strtoupper($overwrite)) { + $overwrite = true; + } elseif ('F' == strtoupper($overwrite)) { + $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); + else { + throw new Exception\BadRequest('The HTTP Overwrite header should be either T or F'); + } + list($destinationDir) = Uri\split($destination); try { $destinationParent = $this->tree->getNodeForPath($destinationDir); - if (!($destinationParent instanceof ICollection)) throw new Exception\UnsupportedMediaType('The destination node is not a collection'); + 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'); - + 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 . '/') { + 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, + 'destination' => $destination, + 'destinationExists' => (bool) $destinationNode, + 'destinationNode' => $destinationNode, ]; - } /** - * Returns a list of properties for a path + * 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 @@ -793,18 +785,18 @@ class Server extends EventEmitter implements LoggerAwareInterface { * returned. * * @param string $path - * @param array $propertyNames + * @param array $propertyNames + * * @return array */ - function getProperties($path, $propertyNames) { - + public function getProperties($path, $propertyNames) + { $result = $this->getPropertiesForPath($path, $propertyNames, 0); if (isset($result[0][200])) { return $result[0][200]; } else { return []; } - } /** @@ -816,26 +808,27 @@ class Server extends EventEmitter implements LoggerAwareInterface { * The parent node will not be returned. * * @param string $path - * @param array $propertyNames + * @param array $propertyNames + * * @return array */ - function getPropertiesForChildren($path, $propertyNames) { - + public function getPropertiesForChildren($path, $propertyNames) + { $result = []; foreach ($this->getPropertiesForPath($path, $propertyNames, 1) as $k => $row) { - // Skipping the parent path - if ($k === 0) continue; + if (0 === $k) { + continue; + } $result[$row['href']] = $row[200]; - } - return $result; + return $result; } /** - * Returns a list of HTTP headers for a particular resource + * 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 @@ -844,81 +837,84 @@ class Server extends EventEmitter implements LoggerAwareInterface { * The headers are intended to be used for HEAD and GET requests. * * @param string $path + * * @return array */ - function getHTTPHeaders($path) { - + public function getHTTPHeaders($path) + { $propertyMap = [ - '{DAV:}getcontenttype' => 'Content-Type', + '{DAV:}getcontenttype' => 'Content-Type', '{DAV:}getcontentlength' => 'Content-Length', - '{DAV:}getlastmodified' => 'Last-Modified', - '{DAV:}getetag' => 'ETag', + '{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 (!isset($properties[$property])) { + continue; + } if (is_scalar($properties[$property])) { $headers[$header] = $properties[$property]; // GetLastModified gets special cased } elseif ($properties[$property] instanceof Xml\Property\GetLastModified) { - $headers[$header] = HTTP\Util::toHTTPDate($properties[$property]->getTime()); + $headers[$header] = HTTP\toDate($properties[$property]->getTime()); } - } return $headers; - } /** * Small helper to support PROPFIND with DEPTH_INFINITY. * * @param PropFind $propFind - * @param array $yieldFirst - * @return \Iterator + * @param array $yieldFirst + * + * @return \Traversable */ - private function generatePathNodes(PropFind $propFind, array $yieldFirst = null) { - if ($yieldFirst !== null) { + private function generatePathNodes(PropFind $propFind, array $yieldFirst = null) + { + if (null !== $yieldFirst) { yield $yieldFirst; } $newDepth = $propFind->getDepth(); $path = $propFind->getPath(); - if ($newDepth !== self::DEPTH_INFINITY) { - $newDepth--; + if (self::DEPTH_INFINITY !== $newDepth) { + --$newDepth; } + $propertyNames = $propFind->getRequestedProperties(); + $propFindType = !empty($propertyNames) ? PropFind::NORMAL : PropFind::ALLPROPS; + foreach ($this->tree->getChildren($path) as $childNode) { - $subPropFind = clone $propFind; - $subPropFind->setDepth($newDepth); - if ($path !== '') { - $subPath = $path . '/' . $childNode->getName(); + if ('' !== $path) { + $subPath = $path.'/'.$childNode->getName(); } else { $subPath = $childNode->getName(); } - $subPropFind->setPath($subPath); + $subPropFind = new PropFind($subPath, $propertyNames, $newDepth, $propFindType); yield [ $subPropFind, - $childNode + $childNode, ]; - if (($newDepth === self::DEPTH_INFINITY || $newDepth >= 1) && $childNode instanceof ICollection) { + if ((self::DEPTH_INFINITY === $newDepth || $newDepth >= 1) && $childNode instanceof ICollection) { foreach ($this->generatePathNodes($subPropFind) as $subItem) { yield $subItem; } } - } } /** - * Returns a list of properties for a given path + * 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 @@ -927,20 +923,21 @@ class Server extends EventEmitter implements LoggerAwareInterface { * If a depth of 1 is requested child elements will also be returned. * * @param string $path - * @param array $propertyNames - * @param int $depth + * @param array $propertyNames + * @param int $depth + * * @return array * * @deprecated Use getPropertiesIteratorForPath() instead (as it's more memory efficient) * @see getPropertiesIteratorForPath() */ - function getPropertiesForPath($path, $propertyNames = [], $depth = 0) { - + public function getPropertiesForPath($path, $propertyNames = [], $depth = 0) + { return iterator_to_array($this->getPropertiesIteratorForPath($path, $propertyNames, $depth)); - } + /** - * Returns a list of properties for a given path + * 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 @@ -949,33 +946,35 @@ class Server extends EventEmitter implements LoggerAwareInterface { * If a depth of 1 is requested child elements will also be returned. * * @param string $path - * @param array $propertyNames - * @param int $depth + * @param array $propertyNames + * @param int $depth + * * @return \Iterator */ - function getPropertiesIteratorForPath($path, $propertyNames = [], $depth = 0) { - + public function getPropertiesIteratorForPath($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; + if (!$this->enablePropfindDepthInfinity && 0 != $depth) { + $depth = 1; + } $path = trim($path, '/'); $propFindType = $propertyNames ? PropFind::NORMAL : PropFind::ALLPROPS; - $propFind = new PropFind($path, (array)$propertyNames, $depth, $propFindType); + $propFind = new PropFind($path, (array) $propertyNames, $depth, $propFindType); $parentNode = $this->tree->getNodeForPath($path); $propFindRequests = [[ $propFind, - $parentNode + $parentNode, ]]; - if (($depth > 0 || $depth === self::DEPTH_INFINITY) && $parentNode instanceof ICollection) { + if (($depth > 0 || self::DEPTH_INFINITY === $depth) && $parentNode instanceof ICollection) { $propFindRequests = $this->generatePathNodes(clone $propFind, current($propFindRequests)); } foreach ($propFindRequests as $propFindRequest) { - list($propFind, $node) = $propFindRequest; $r = $this->getPropertiesByNode($propFind, $node); if ($r) { @@ -992,9 +991,7 @@ class Server extends EventEmitter implements LoggerAwareInterface { } yield $result; } - } - } /** @@ -1009,17 +1006,17 @@ class Server extends EventEmitter implements LoggerAwareInterface { * * @param array $paths * @param array $propertyNames + * * @return array */ - function getPropertiesForMultiplePaths(array $paths, array $propertyNames = []) { - + public 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) { @@ -1031,14 +1028,11 @@ class Server extends EventEmitter implements LoggerAwareInterface { $result[$path]['href'] .= '/'; } } - } return $result; - } - /** * Determines all properties for a node. * @@ -1050,13 +1044,13 @@ class Server extends EventEmitter implements LoggerAwareInterface { * list of properties. * * @param PropFind $propFind - * @param INode $node + * @param INode $node + * * @return bool */ - function getPropertiesByNode(PropFind $propFind, INode $node) { - + public function getPropertiesByNode(PropFind $propFind, INode $node) + { return $this->emit('propFind', [$propFind, $node]); - } /** @@ -1071,13 +1065,16 @@ class Server extends EventEmitter implements LoggerAwareInterface { * @param string $uri * @param resource $data * @param string $etag + * * @return bool */ - function createFile($uri, $data, &$etag = null) { + public function createFile($uri, $data, &$etag = null) + { + list($dir, $name) = Uri\split($uri); - list($dir, $name) = URLUtil::splitPath($uri); - - if (!$this->emit('beforeBind', [$uri])) return false; + if (!$this->emit('beforeBind', [$uri])) { + return false; + } $parent = $this->tree->getNodeForPath($dir); if (!$parent instanceof ICollection) { @@ -1090,13 +1087,17 @@ class Server extends EventEmitter implements LoggerAwareInterface { // // If $modified is true, we must not send back an ETag. $modified = false; - if (!$this->emit('beforeCreateFile', [$uri, &$data, $parent, &$modified])) return false; + if (!$this->emit('beforeCreateFile', [$uri, &$data, $parent, &$modified])) { + return false; + } $etag = $parent->createFile($name, $data); - if ($modified) $etag = null; + if ($modified) { + $etag = null; + } - $this->tree->markDirty($dir . '/' . $name); + $this->tree->markDirty($dir.'/'.$name); $this->emit('afterBind', [$uri]); $this->emit('afterCreateFile', [$uri, $parent]); @@ -1112,10 +1113,11 @@ class Server extends EventEmitter implements LoggerAwareInterface { * @param string $uri * @param resource $data * @param string $etag + * * @return bool */ - function updateFile($uri, $data, &$etag = null) { - + public function updateFile($uri, $data, &$etag = null) + { $node = $this->tree->getNodeForPath($uri); // It is possible for an event handler to modify the content of the @@ -1124,47 +1126,46 @@ class Server extends EventEmitter implements LoggerAwareInterface { // // If $modified is true, we must not send back an ETag. $modified = false; - if (!$this->emit('beforeWriteContent', [$uri, $node, &$data, &$modified])) return false; + if (!$this->emit('beforeWriteContent', [$uri, $node, &$data, &$modified])) { + return false; + } $etag = $node->put($data); - if ($modified) $etag = null; + 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) { - + public function createDirectory($uri) + { $this->createCollection($uri, new MkCol(['{DAV:}collection'], [])); - } /** - * Use this method to create a new collection + * Use this method to create a new collection. + * + * @param string $uri The new uri + * @param MkCol $mkCol * - * @param string $uri The new uri - * @param MkCol $mkCol * @return array|null */ - function createCollection($uri, MkCol $mkCol) { - - list($parentUri, $newName) = URLUtil::splitPath($uri); + public function createCollection($uri, MkCol $mkCol) + { + list($parentUri, $newName) = Uri\split($uri); // 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 @@ -1178,26 +1179,23 @@ class Server extends EventEmitter implements LoggerAwareInterface { // 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) { // NotFound is the expected behavior. } - - if (!$this->emit('beforeBind', [$uri])) return; + if (!$this->emit('beforeBind', [$uri])) { + return; + } if ($parent instanceof IExtendedCollection) { - - /** + /* * If the parent is an instance of IExtendedCollection, it means that * we can pass the MkCol object directly as it may be able to store * properties immediately. */ $parent->createExtendedCollection($newName, $mkCol); - } else { - - /** + /* * If the parent is a standard ICollection, it means only * 'standard' collections can be created, so we should fail any * MKCOL operation that carries extra resourcetypes. @@ -1207,7 +1205,6 @@ class Server extends EventEmitter implements LoggerAwareInterface { } $parent->createDirectory($newName); - } // If there are any properties that have not been handled/stored, @@ -1226,23 +1223,21 @@ class Server extends EventEmitter implements LoggerAwareInterface { ]; foreach ($result as $propertyName => $status) { - if (!isset($formattedResult[$status])) { $formattedResult[$status] = []; } $formattedResult[$status][$propertyName] = null; - } + return $formattedResult; } $this->tree->markDirty($parentUri); $this->emit('afterBind', [$uri]); - } /** - * This method updates a resource's properties + * 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. @@ -1255,17 +1250,17 @@ class Server extends EventEmitter implements LoggerAwareInterface { * as their values. * * @param string $path - * @param array $properties + * @param array $properties + * * @return array */ - function updateProperties($path, array $properties) { - + public function updateProperties($path, array $properties) + { $propPatch = new PropPatch($properties); $this->emit('propPatch', [$path, $propPatch]); $propPatch->commit(); return $propPatch->getResult(); - } /** @@ -1286,19 +1281,19 @@ class Server extends EventEmitter implements LoggerAwareInterface { * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function checkPreconditions(RequestInterface $request, ResponseInterface $response) { - + public 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 @@ -1310,13 +1305,11 @@ class Server extends EventEmitter implements LoggerAwareInterface { } // Only need to check entity tags if they are not * - if ($ifMatch !== '*') { - + if ('*' !== $ifMatch) { // There can be multiple ETags $ifMatch = explode(',', $ifMatch); $haveMatch = false; foreach ($ifMatch as $ifMatchItem) { - // Stripping any extra spaces $ifMatchItem = trim($ifMatchItem, ' '); @@ -1330,17 +1323,17 @@ class Server extends EventEmitter implements LoggerAwareInterface { $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 ($etag) { + $response->setHeader('ETag', $etag); + } + throw new Exception\PreconditionFailed('An If-Match header was specified, but none of the specified 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 @@ -1355,46 +1348,46 @@ class Server extends EventEmitter implements LoggerAwareInterface { } if ($nodeExists) { $haveMatch = false; - if ($ifNoneMatch === '*') $haveMatch = true; - else { - + if ('*' === $ifNoneMatch) { + $haveMatch = true; + } else { // There might be multiple ETags $ifNoneMatch = explode(',', $ifNoneMatch); $etag = $node instanceof IFile ? $node->getETag() : null; foreach ($ifNoneMatch as $ifNoneMatchItem) { - // Stripping any extra spaces $ifNoneMatchItem = trim($ifNoneMatchItem, ' '); - if ($etag === $ifNoneMatchItem) $haveMatch = true; - + if ($etag === $ifNoneMatchItem) { + $haveMatch = true; + } } - } if ($haveMatch) { - if ($etag) $response->setHeader('ETag', $etag); - if ($request->getMethod() === 'GET') { + if ($etag) { + $response->setHeader('ETag', $etag); + } + if ('GET' === $request->getMethod()) { $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); + $date = HTTP\parseDate($ifModifiedSince); if ($date) { if (is_null($node)) { @@ -1402,10 +1395,11 @@ class Server extends EventEmitter implements LoggerAwareInterface { } $lastMod = $node->getLastModified(); if ($lastMod) { - $lastMod = new \DateTime('@' . $lastMod); + $lastMod = new \DateTime('@'.$lastMod); if ($lastMod <= $date) { $response->setStatus(304); - $response->setHeader('Last-Modified', HTTP\Util::toHTTPDate($lastMod)); + $response->setHeader('Last-Modified', HTTP\toDate($lastMod)); + return false; } } @@ -1413,10 +1407,9 @@ class Server extends EventEmitter implements LoggerAwareInterface { } 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); + $date = HTTP\parseDate($ifUnmodifiedSince); // We must only check the date if it's valid if ($date) { @@ -1425,13 +1418,12 @@ class Server extends EventEmitter implements LoggerAwareInterface { } $lastMod = $node->getLastModified(); if ($lastMod) { - $lastMod = new \DateTime('@' . $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 @@ -1460,13 +1452,11 @@ class Server extends EventEmitter implements LoggerAwareInterface { // 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; @@ -1476,35 +1466,28 @@ class Server extends EventEmitter implements LoggerAwareInterface { // Checking the ETag, only if the token was already deemed // 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'); - + 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 + * 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 @@ -1572,12 +1555,15 @@ class Server extends EventEmitter implements LoggerAwareInterface { * ] * * @param RequestInterface $request + * * @return array */ - function getIfConditions(RequestInterface $request) { - + public function getIfConditions(RequestInterface $request) + { $header = $request->getHeader('If'); - if (!$header) return []; + if (!$header) { + return []; + } $matches = []; @@ -1587,18 +1573,16 @@ class Server extends EventEmitter implements LoggerAwareInterface { $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'] : '' + 'token' => $match['token'], + 'etag' => isset($match['etag']) ? $match['etag'] : '', ]; } else { - if (!$match['uri']) { $realUri = $request->getPath(); } else { @@ -1606,55 +1590,55 @@ class Server extends EventEmitter implements LoggerAwareInterface { } $conditions[] = [ - 'uri' => $realUri, + 'uri' => $realUri, 'tokens' => [ [ 'negate' => $match['not'] ? true : false, - 'token' => $match['token'], - 'etag' => isset($match['etag']) ? $match['etag'] : '' - ] + '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) { - + public function getResourceTypeForNode(INode $node) + { $result = []; foreach ($this->resourceTypeMapping as $className => $resourceType) { - if ($node instanceof $className) $result[] = $resourceType; + if ($node instanceof $className) { + $result[] = $resourceType; + } } - return $result; + 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|\Traversable $fileProperties The list with nodes - * @param bool $strip404s + * @param bool $strip404s + * * @return string */ - function generateMultiStatus($fileProperties, $strip404s = false) { - + public function generateMultiStatus($fileProperties, $strip404s = false) + { $w = $this->xml->getWriter(); $w->openMemory(); $w->contextUri = $this->baseUri; @@ -1663,7 +1647,6 @@ class Server extends EventEmitter implements LoggerAwareInterface { $w->startElement('{DAV:}multistatus'); foreach ($fileProperties as $entry) { - $href = $entry['href']; unset($entry['href']); if ($strip404s) { @@ -1674,14 +1657,12 @@ class Server extends EventEmitter implements LoggerAwareInterface { $entry ); $w->write([ - 'name' => '{DAV:}response', - 'value' => $response + 'name' => '{DAV:}response', + 'value' => $response, ]); } $w->endElement(); return $w->outputMemory(); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/ServerPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/ServerPlugin.php index b2c468ab387..9aefa7f72dc 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/ServerPlugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/ServerPlugin.php @@ -1,5 +1,7 @@ $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => null, - 'link' => null, + 'link' => null, ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Sharing/ISharedNode.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Sharing/ISharedNode.php index 034aefbdce6..a746ac75350 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Sharing/ISharedNode.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Sharing/ISharedNode.php @@ -1,5 +1,7 @@ server = $server; $server->xml->elementMap['{DAV:}share-resource'] = 'Sabre\\DAV\\Xml\\Request\\ShareResource'; @@ -94,12 +93,11 @@ class Plugin extends ServerPlugin { '{DAV:}share-mode' ); - $server->on('method:POST', [$this, 'httpPost']); - $server->on('propFind', [$this, 'propFind']); + $server->on('method:POST', [$this, 'httpPost']); + $server->on('propFind', [$this, 'propFind']); $server->on('getSupportedPrivilegeSet', [$this, 'getSupportedPrivilegeSet']); - $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); - $server->on('onBrowserPostAction', [$this, 'browserPostAction']); - + $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); + $server->on('onBrowserPostAction', [$this, 'browserPostAction']); } /** @@ -108,18 +106,15 @@ class Plugin extends ServerPlugin { * The sharees array is a list of people that are to be added modified * or removed in the list of shares. * - * @param string $path + * @param string $path * @param Sharee[] $sharees - * @return void */ - function shareResource($path, array $sharees) { - + public function shareResource($path, array $sharees) + { $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof ISharedNode) { - throw new Forbidden('Sharing is not allowed on this node'); - } // Getting ACL info @@ -138,7 +133,6 @@ class Plugin extends ServerPlugin { $sharee->principal = $principal; } $node->updateInvites($sharees); - } /** @@ -147,47 +141,41 @@ class Plugin extends ServerPlugin { * This allows us to inject any sharings-specific properties. * * @param PropFind $propFind - * @param INode $node - * @return void + * @param INode $node */ - function propFind(PropFind $propFind, INode $node) { - + public function propFind(PropFind $propFind, INode $node) + { if ($node instanceof ISharedNode) { - - $propFind->handle('{DAV:}share-access', function() use ($node) { - + $propFind->handle('{DAV:}share-access', function () use ($node) { return new Property\ShareAccess($node->getShareAccess()); - }); - $propFind->handle('{DAV:}invite', function() use ($node) { - + $propFind->handle('{DAV:}invite', function () use ($node) { return new Property\Invite($node->getInvites()); - }); - $propFind->handle('{DAV:}share-resource-uri', function() use ($node) { - + $propFind->handle('{DAV:}share-resource-uri', function () use ($node) { return new Property\Href($node->getShareResourceUri()); - }); - } - } /** - * We intercept this to handle POST requests on shared resources + * We intercept this to handle POST requests on shared resources. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return null|bool + * + * @return bool|null */ - function httpPost(RequestInterface $request, ResponseInterface $response) { - + public function httpPost(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); $contentType = $request->getHeader('Content-Type'); + if (null === $contentType) { + return; + } // We're only interested in the davsharing content type. - if (strpos($contentType, 'application/davsharing+xml') === false) { + if (false === strpos($contentType, 'application/davsharing+xml')) { return; } @@ -198,7 +186,6 @@ class Plugin extends ServerPlugin { ); switch ($documentType) { - case '{DAV:}share-resource': $this->shareResource($path, $message->sharees); @@ -210,11 +197,9 @@ class Plugin extends ServerPlugin { // Breaking the event chain return false; - default : - throw new BadRequest('Unexpected document type: ' . $documentType . ' for this Content-Type'); - + default: + throw new BadRequest('Unexpected document type: '.$documentType.' for this Content-Type'); } - } /** @@ -226,11 +211,11 @@ class Plugin extends ServerPlugin { * @param INode $node * @param array $supportedPrivilegeSet */ - function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) { - + public function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet) + { if ($node instanceof ISharedNode) { $supportedPrivilegeSet['{DAV:}share'] = [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ]; } @@ -247,27 +232,27 @@ class Plugin extends ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'This plugin implements WebDAV resource sharing', - 'link' => 'https://github.com/evert/webdav-sharing' + 'link' => 'https://github.com/evert/webdav-sharing', ]; - } /** * This method is used to generate HTML output for the * DAV\Browser\Plugin. * - * @param INode $node + * @param INode $node * @param string $output * @param string $path + * * @return bool|null */ - function htmlActionsPanel(INode $node, &$output, $path) { - + public function htmlActionsPanel(INode $node, &$output, $path) + { if (!$node instanceof ISharedNode) { return; } @@ -293,7 +278,6 @@ class Plugin extends ServerPlugin { '; - } /** @@ -302,11 +286,11 @@ class Plugin extends ServerPlugin { * * @param string $path * @param string $action - * @param array $postVars + * @param array $postVars */ - function browserPostAction($path, $action, $postVars) { - - if ($action !== 'share') { + public function browserPostAction($path, $action, $postVars) + { + if ('share' !== $action) { return; } @@ -319,7 +303,7 @@ class Plugin extends ServerPlugin { $accessMap = [ 'readwrite' => self::ACCESS_READWRITE, - 'read' => self::ACCESS_READ, + 'read' => self::ACCESS_READ, 'no-access' => self::ACCESS_NOACCESS, ]; @@ -327,7 +311,7 @@ class Plugin extends ServerPlugin { throw new BadRequest('The "access" POST must be readwrite, read or no-access'); } $sharee = new Sharee([ - 'href' => $postVars['href'], + 'href' => $postVars['href'], 'access' => $accessMap[$postVars['access']], ]); @@ -335,8 +319,7 @@ class Plugin extends ServerPlugin { $path, [$sharee] ); + return false; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleCollection.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleCollection.php index 998cfcbff5f..1fbb6982dc6 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleCollection.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleCollection.php @@ -1,9 +1,13 @@ name = $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'); + foreach ($children as $key => $child) { + if (is_string($child)) { + $child = new SimpleFile($key, $child); + } elseif (is_array($child)) { + $child = new self($key, $child); + } elseif (!$child instanceof INode) { + throw new InvalidArgumentException('Children must be specified as strings, arrays or instances of Sabre\DAV\INode'); + } $this->addChild($child); - } - } /** - * Adds a new childnode to this collection + * Adds a new childnode to this collection. * * @param INode $child - * @return void */ - function addChild(INode $child) { - + public function addChild(INode $child) + { $this->children[$child->getName()] = $child; - } /** - * Returns the name of the collection + * Returns the name of the collection. * * @return string */ - function getName() { - + public function getName() + { return $this->name; - } /** @@ -82,26 +86,26 @@ class SimpleCollection extends Collection { * 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() . '\''); - + public 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 + * Returns a list of children for this collection. * * @return INode[] */ - function getChildren() { - + public function getChildren() + { return array_values($this->children); - } - - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleFile.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleFile.php index bcad786f34a..ca808b67205 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleFile.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleFile.php @@ -1,9 +1,11 @@ name = $name; $this->contents = $contents; $this->mimeType = $mimeType; - } /** @@ -61,23 +62,21 @@ class SimpleFile extends File { * * @return string */ - function getName() { - + public function getName() + { return $this->name; - } /** - * Returns the data + * Returns the data. * * This method may either return a string or a readable stream resource * * @return mixed */ - function get() { - + public function get() + { return $this->contents; - } /** @@ -85,37 +84,35 @@ class SimpleFile extends File { * * @return int */ - function getSize() { - + public function getSize() + { return strlen($this->contents); - } /** - * Returns the ETag for a file + * 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 '"' . sha1($this->contents) . '"'; - + public function getETag() + { + return '"'.sha1($this->contents).'"'; } /** - * Returns the mime-type for a file + * Returns the mime-type for a file. * * If null is returned, we'll assume application/octet-stream + * * @return string */ - function getContentType() { - + public function getContentType() + { return $this->mimeType; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/StringUtil.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/StringUtil.php index 10eecebfd47..13a4399e30d 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/StringUtil.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/StringUtil.php @@ -1,9 +1,11 @@ server = $server; $server->xml->elementMap['{DAV:}sync-collection'] = 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport'; $self = $this; - $server->on('report', function($reportName, $dom, $uri) use ($self) { - - if ($reportName === '{DAV:}sync-collection') { + $server->on('report', function ($reportName, $dom, $uri) use ($self) { + if ('{DAV:}sync-collection' === $reportName) { $this->server->transactionType = 'report-sync-collection'; $self->syncCollection($uri, $dom); + return false; } - }); - $server->on('propFind', [$this, 'propFind']); + $server->on('propFind', [$this, 'propFind']); $server->on('validateTokens', [$this, 'validateTokens']); - } /** @@ -81,10 +79,11 @@ class Plugin extends DAV\ServerPlugin { * implement them * * @param string $uri + * * @return array */ - function getSupportedReportSet($uri) { - + public function getSupportedReportSet($uri) + { $node = $this->server->tree->getNodeForPath($uri); if ($node instanceof ISyncCollection && $node->getSyncToken()) { return [ @@ -93,19 +92,16 @@ class Plugin extends DAV\ServerPlugin { } return []; - } - /** * This method handles the {DAV:}sync-collection HTTP REPORT. * - * @param string $uri + * @param string $uri * @param SyncCollectionReport $report - * @return void */ - function syncCollection($uri, SyncCollectionReport $report) { - + public function syncCollection($uri, SyncCollectionReport $report) + { // Getting the data $node = $this->server->tree->getNodeForPath($uri); if (!$node instanceof ISyncCollection) { @@ -119,19 +115,16 @@ class Plugin extends DAV\ServerPlugin { $syncToken = $report->syncToken; if (!is_null($syncToken)) { // Sync-token must start with our prefix - if (substr($syncToken, 0, strlen(self::SYNCTOKEN_PREFIX)) !== self::SYNCTOKEN_PREFIX) { + if (self::SYNCTOKEN_PREFIX !== substr($syncToken, 0, strlen(self::SYNCTOKEN_PREFIX))) { throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); } $syncToken = substr($syncToken, strlen(self::SYNCTOKEN_PREFIX)); - } $changeInfo = $node->getChanges($syncToken, $report->syncLevel, $report->limit); if (is_null($changeInfo)) { - throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token'); - } // Encoding the response @@ -143,7 +136,6 @@ class Plugin extends DAV\ServerPlugin { $changeInfo['deleted'], $report->properties ); - } /** @@ -151,50 +143,41 @@ class Plugin extends DAV\ServerPlugin { * * @param string $syncToken * @param string $collectionUrl - * @param array $added - * @param array $modified - * @param array $deleted - * @param array $properties - * @return void + * @param array $added + * @param array $modified + * @param array $deleted + * @param array $properties */ - protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties) { - - + protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties) + { $fullPaths = []; // Pre-fetching children, if this is possible. foreach (array_merge($added, $modified) as $item) { - $fullPath = $collectionUrl . '/' . $item; + $fullPath = $collectionUrl.'/'.$item; $fullPaths[] = $fullPath; } $responses = []; foreach ($this->server->getPropertiesForMultiplePaths($fullPaths, $properties) as $fullPath => $props) { - // The 'Property_Response' class is responsible for generating a // single {DAV:}response xml element. $responses[] = new DAV\Xml\Element\Response($fullPath, $props); - } - - // 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; + $fullPath = $collectionUrl.'/'.$item; $responses[] = new DAV\Xml\Element\Response($fullPath, [], 404); - } - $multiStatus = new DAV\Xml\Response\MultiStatus($responses, self::SYNCTOKEN_PREFIX . $syncToken); + $multiStatus = new DAV\Xml\Response\MultiStatus($responses, self::SYNCTOKEN_PREFIX.$syncToken); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setBody( $this->server->xml->write('{DAV:}multistatus', $multiStatus, $this->server->getBaseUri()) ); - } /** @@ -202,18 +185,17 @@ class Plugin extends DAV\ServerPlugin { * We intercept this to see if we must return a {DAV:}sync-token. * * @param DAV\PropFind $propFind - * @param DAV\INode $node - * @return void + * @param DAV\INode $node */ - function propFind(DAV\PropFind $propFind, DAV\INode $node) { - - $propFind->handle('{DAV:}sync-token', function() use ($node) { + public 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; - }); + return self::SYNCTOKEN_PREFIX.$token; + }); } /** @@ -223,17 +205,14 @@ class Plugin extends DAV\ServerPlugin { * in the If: header, and check if they are valid. * * @param RequestInterface $request - * @param array $conditions - * @return void + * @param array $conditions */ - function validateTokens(RequestInterface $request, &$conditions) { - + public 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) { + if (self::SYNCTOKEN_PREFIX !== substr($token['token'], 0, strlen(self::SYNCTOKEN_PREFIX))) { continue; } @@ -246,11 +225,8 @@ class Plugin extends DAV\ServerPlugin { ) { $conditions[$kk]['tokens'][$ii]['validToken'] = true; } - } - } - } /** @@ -264,14 +240,12 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds support for WebDAV Collection Sync (rfc6578)', - 'link' => 'http://sabre.io/dav/sync/', + 'link' => 'http://sabre.io/dav/sync/', ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php index 7b453d10536..6cf772f445a 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php @@ -1,13 +1,15 @@ dataDir = $dataDir; - } /** - * Initialize the plugin + * 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) { - + public function initialize(Server $server) + { $this->server = $server; - $server->on('beforeMethod', [$this, 'beforeMethod']); + $server->on('beforeMethod:*', [$this, 'beforeMethod']); $server->on('beforeCreateFile', [$this, 'beforeCreateFile']); - } /** - * This method is called before any HTTP method handler + * 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 RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function beforeMethod(RequestInterface $request, ResponseInterface $response) { - - if (!$tempLocation = $this->isTempFile($request->getPath())) + public function beforeMethod(RequestInterface $request, ResponseInterface $response) + { + if (!$tempLocation = $this->isTempFile($request->getPath())) { return; + } switch ($request->getMethod()) { - case 'GET' : + case 'GET': return $this->httpGet($request, $response, $tempLocation); - case 'PUT' : + case 'PUT': return $this->httpPut($request, $response, $tempLocation); - case 'PROPFIND' : + case 'PROPFIND': return $this->httpPropfind($request, $response, $tempLocation); - case 'DELETE' : + case 'DELETE': return $this->httpDelete($request, $response, $tempLocation); } - return; + return; } /** @@ -132,24 +137,25 @@ class TemporaryFileFilterPlugin extends ServerPlugin { * This is used to deal with HTTP LOCK requests which create a new * file. * - * @param string $uri - * @param resource $data + * @param string $uri + * @param resource $data * @param ICollection $parent - * @param bool $modified Should be set to true, if this event handler - * changed &$data. + * @param bool $modified should be set to true, if this event handler + * changed &$data + * * @return bool */ - function beforeCreateFile($uri, $data, ICollection $parent, $modified) { - + public function beforeCreateFile($uri, $data, ICollection $parent, $modified) + { if ($tempPath = $this->isTempFile($uri)) { - $hR = $this->server->httpResponse; $hR->setHeader('X-Sabre-Temp', 'true'); file_put_contents($tempPath, $data); + return false; } - return; + return; } /** @@ -158,71 +164,76 @@ class TemporaryFileFilterPlugin extends ServerPlugin { * temporary file storage. * * @param string $path + * * @return bool|string */ - protected function isTempFile($path) { - + protected function isTempFile($path) + { // We're only interested in the basename. - list(, $tempPath) = URLUtil::splitPath($path); + list(, $tempPath) = Uri\split($path); + + if (null === $tempPath) { + return false; + } foreach ($this->temporaryFilePatterns as $tempFile) { - if (preg_match($tempFile, $tempPath)) { - return $this->getDataDir() . '/sabredav_' . md5($path) . '.tempfile'; + 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 RequestInterface $request * @param ResponseInterface $hR - * @param string $tempLocation + * @param string $tempLocation + * * @return bool */ - function httpGet(RequestInterface $request, ResponseInterface $hR, $tempLocation) { - - if (!file_exists($tempLocation)) return; + public function httpGet(RequestInterface $request, ResponseInterface $hR, $tempLocation) + { + if (!file_exists($tempLocation)) { + return; + } $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; + return false; } /** * This method handles the PUT method. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $hR - * @param string $tempLocation + * @param string $tempLocation + * * @return bool */ - function httpPut(RequestInterface $request, ResponseInterface $hR, $tempLocation) { - + public 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'); + 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; + return false; } /** @@ -231,20 +242,23 @@ class TemporaryFileFilterPlugin extends ServerPlugin { * If the file didn't exist, it will return false, which will make the * standard HTTP DELETE handler kick in. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $hR - * @param string $tempLocation + * @param string $tempLocation + * * @return bool */ - function httpDelete(RequestInterface $request, ResponseInterface $hR, $tempLocation) { - - if (!file_exists($tempLocation)) return; + public function httpDelete(RequestInterface $request, ResponseInterface $hR, $tempLocation) + { + if (!file_exists($tempLocation)) { + return; + } unlink($tempLocation); $hR->setHeader('X-Sabre-Temp', 'true'); $hR->setStatus(204); - return false; + return false; } /** @@ -254,14 +268,17 @@ class TemporaryFileFilterPlugin extends ServerPlugin { * for which properties were requested, and just sends back a default * set of properties. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $hR - * @param string $tempLocation + * @param string $tempLocation + * * @return bool */ - function httpPropfind(RequestInterface $request, ResponseInterface $hR, $tempLocation) { - - if (!file_exists($tempLocation)) return; + public function httpPropfind(RequestInterface $request, ResponseInterface $hR, $tempLocation) + { + if (!file_exists($tempLocation)) { + return; + } $hR->setHeader('X-Sabre-Temp', 'true'); $hR->setStatus(207); @@ -269,22 +286,20 @@ class TemporaryFileFilterPlugin extends ServerPlugin { $properties = [ 'href' => $request->getPath(), - 200 => [ - '{DAV:}getlastmodified' => new Xml\Property\GetLastModified(filemtime($tempLocation)), - '{DAV:}getcontentlength' => filesize($tempLocation), - '{DAV:}resourcetype' => new Xml\Property\ResourceType(null), - '{' . Server::NS_SABREDAV . '}tempFile' => true, - + 200 => [ + '{DAV:}getlastmodified' => new Xml\Property\GetLastModified(filemtime($tempLocation)), + '{DAV:}getcontentlength' => filesize($tempLocation), + '{DAV:}resourcetype' => new Xml\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. * diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Tree.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Tree.php index 5d2792503f9..7a5a25f8707 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Tree.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Tree.php @@ -1,8 +1,10 @@ rootNode = $rootNode; - } /** - * Returns the INode object for the requested path + * Returns the INode object for the requested path. * * @param string $path + * * @return INode */ - function getNodeForPath($path) { - + public function getNodeForPath($path) + { $path = trim($path, '/'); - if (isset($this->cache[$path])) return $this->cache[$path]; + if (isset($this->cache[$path])) { + return $this->cache[$path]; + } // Is it the root node? if (!strlen($path)) { @@ -61,25 +65,24 @@ class Tree { } // Attempting to fetch its parent - list($parentName, $baseName) = URLUtil::splitPath($path); + list($parentName, $baseName) = Uri\split($path); // If there was no parent, we must simply ask it from the root node. - if ($parentName === "") { + 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); - + 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; + return $node; } /** @@ -89,61 +92,64 @@ class Tree { * it cheaper. * * @param string $path + * * @return bool */ - function nodeExists($path) { - + public function nodeExists($path) + { try { - // The root always exists - if ($path === '') return true; + if ('' === $path) { + return true; + } - list($parent, $base) = URLUtil::splitPath($path); + list($parent, $base) = Uri\split($path); $parentNode = $this->getNodeForPath($parent); - if (!$parentNode instanceof ICollection) return false; + if (!$parentNode instanceof ICollection) { + return false; + } + return $parentNode->childExists($base); - } catch (Exception\NotFound $e) { - return false; - } - } /** - * Copies a file from path to another + * Copies a file from path to another. * - * @param string $sourcePath The source location + * @param string $sourcePath The source location * @param string $destinationPath The full destination path - * @return void */ - function copy($sourcePath, $destinationPath) { - + public function copy($sourcePath, $destinationPath) + { $sourceNode = $this->getNodeForPath($sourcePath); // grab the dirname and basename components - list($destinationDir, $destinationName) = URLUtil::splitPath($destinationPath); + list($destinationDir, $destinationName) = Uri\split($destinationPath); $destinationParent = $this->getNodeForPath($destinationDir); - $this->copyNode($sourceNode, $destinationParent, $destinationName); + // Check if the target can handle the copy itself. If not, we do it ourselves. + if (!$destinationParent instanceof ICopyTarget || !$destinationParent->copyInto($destinationName, $sourcePath, $sourceNode)) { + $this->copyNode($sourceNode, $destinationParent, $destinationName); + } $this->markDirty($destinationDir); - } /** - * Moves a file from one location to another + * Moves a file from one location to another. * - * @param string $sourcePath The path to the file which should be moved + * @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); + public function move($sourcePath, $destinationPath) + { + list($sourceDir) = Uri\split($sourcePath); + list($destinationDir, $destinationName) = Uri\split($destinationPath); if ($sourceDir === $destinationDir) { // If this is a 'local' rename, it means we can just trigger a rename. @@ -164,49 +170,45 @@ class Tree { } $this->markDirty($sourceDir); $this->markDirty($destinationDir); - } /** - * Deletes a node from the tree + * Deletes a node from the tree. * * @param string $path - * @return void */ - function delete($path) { - + public function delete($path) + { $node = $this->getNodeForPath($path); $node->delete(); - list($parent) = URLUtil::splitPath($path); + list($parent) = Uri\split($path); $this->markDirty($parent); - } /** * Returns a list of childnodes for a given path. * * @param string $path - * @return array + * + * @return \Traversable */ - function getChildren($path) { - + public 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; - + if ('' !== $basePath) { + $basePath .= '/'; } - return $children; + foreach ($node->getChildren() as $child) { + $this->cache[$basePath.$child->getName()] = $child; + yield $child; + } } /** - * This method is called with every tree update + * This method is called with every tree update. * * Examples of tree updates are: * * node deletions @@ -221,19 +223,17 @@ class Tree { * If a path is passed, it is assumed that the entire subtree is dirty * * @param string $path - * @return void */ - function markDirty($path) { - + public 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 ($path === '' || $nodePath == $path || strpos($nodePath, $path . '/') === 0) + if ('' === $path || $nodePath == $path || 0 === strpos($nodePath, $path.'/')) { unset($this->cache[$nodePath]); - + } } - } /** @@ -247,15 +247,16 @@ class Tree { * 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. + * @param array $paths list of nodes that must be fetched + * * @return array */ - function getMultipleNodes($paths) { - + public function getMultipleNodes($paths) + { // Finding common parents $parents = []; foreach ($paths as $path) { - list($parent, $node) = URLUtil::splitPath($path); + list($parent, $node) = Uri\split($path); if (!isset($parents[$parent])) { $parents[$parent] = [$node]; } else { @@ -266,42 +267,38 @@ class Tree { $result = []; foreach ($parents as $parent => $children) { - $parentNode = $this->getNodeForPath($parent); if ($parentNode instanceof IMultiGet) { foreach ($parentNode->getMultipleChildren($children) as $childNode) { - $fullPath = $parent . '/' . $childNode->getName(); + $fullPath = $parent.'/'.$childNode->getName(); $result[$fullPath] = $childNode; $this->cache[$fullPath] = $childNode; } } else { foreach ($children as $child) { - $fullPath = $parent . '/' . $child; + $fullPath = $parent.'/'.$child; $result[$fullPath] = $this->getNodeForPath($fullPath); } } - } return $result; - } - /** - * copyNode + * copyNode. * - * @param INode $source + * @param INode $source * @param ICollection $destinationParent - * @param string $destinationName - * @return void + * @param string $destinationName */ - protected function copyNode(INode $source, ICollection $destinationParent, $destinationName = null) { - - if (!$destinationName) $destinationName = $source->getName(); + protected function copyNode(INode $source, ICollection $destinationParent, $destinationName = null) + { + if ('' === (string) $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 @@ -313,28 +310,19 @@ class Tree { } $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(); - } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/UUIDUtil.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/UUIDUtil.php index 177adafd3b8..8c36e1b0b81 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/UUIDUtil.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/UUIDUtil.php @@ -1,9 +1,11 @@ isEmptyElement) { $reader->next(); + return []; } @@ -52,22 +56,17 @@ class Prop implements XmlDeserializable { $reader->read(); do { - - if ($reader->nodeType === Reader::ELEMENT) { - + if (Reader::ELEMENT === $reader->nodeType) { $clark = $reader->getClark(); $values[$clark] = self::parseCurrentElement($reader)['value']; - } else { $reader->read(); } - - } while ($reader->nodeType !== Reader::END_ELEMENT); + } while (Reader::END_ELEMENT !== $reader->nodeType); $reader->read(); return $values; - } /** @@ -81,10 +80,11 @@ class Prop implements XmlDeserializable { * * value - The parsed value. * * @param Reader $reader + * * @return array */ - private static function parseCurrentElement(Reader $reader) { - + private static function parseCurrentElement(Reader $reader) + { $name = $reader->getClark(); if (array_key_exists($name, $reader->elementMap)) { @@ -95,22 +95,20 @@ class Prop implements XmlDeserializable { $value = call_user_func($deserializer, $reader); } else { $type = gettype($deserializer); - if ($type === 'string') { - $type .= ' (' . $deserializer . ')'; - } elseif ($type === 'object') { - $type .= ' (' . get_class($deserializer) . ')'; + if ('string' === $type) { + $type .= ' ('.$deserializer.')'; + } elseif ('object' === $type) { + $type .= ' ('.get_class($deserializer).')'; } - throw new \LogicException('Could not use this type as a deserializer: ' . $type); + throw new \LogicException('Could not use this type as a deserializer: '.$type); } } else { $value = Complex::xmlDeserialize($reader); } return [ - 'name' => $name, + 'name' => $name, 'value' => $value, ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Response.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Response.php index ce97ae94366..a110918094d 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Response.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Response.php @@ -1,5 +1,7 @@ href = $href; $this->responseProperties = $responseProperties; $this->httpStatus = $httpStatus; - } /** - * Returns the url + * Returns the url. * * @return string */ - function getHref() { - + public function getHref() + { return $this->href; - } /** - * Returns the httpStatus value + * Returns the httpStatus value. * * @return string */ - function getHttpStatus() { - + public function getHttpStatus() + { return $this->httpStatus; - } /** - * Returns the property list + * Returns the property list. * * @return array */ - function getResponseProperties() { - + public function getResponseProperties() + { return $this->responseProperties; - } - /** * The serialize method is called during xml writing. * @@ -115,19 +112,17 @@ class Response implements Element { * responsible for closing them. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { if ($status = $this->getHTTPStatus()) { - $writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]); + $writer->writeElement('{DAV:}status', 'HTTP/1.1 '.$status.' '.\Sabre\HTTP\Response::$statusCodes[$status]); } - $writer->writeElement('{DAV:}href', $writer->contextUri . \Sabre\HTTP\encodePath($this->getHref())); + $writer->writeElement('{DAV:}href', $writer->contextUri.\Sabre\HTTP\encodePath($this->getHref())); $empty = true; foreach ($this->getResponseProperties() as $status => $properties) { - // Skipping empty lists if (!$properties || (!ctype_digit($status) && !is_int($status))) { continue; @@ -135,9 +130,8 @@ class Response implements Element { $empty = false; $writer->startElement('{DAV:}propstat'); $writer->writeElement('{DAV:}prop', $properties); - $writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]); + $writer->writeElement('{DAV:}status', 'HTTP/1.1 '.$status.' '.\Sabre\HTTP\Response::$statusCodes[$status]); $writer->endElement(); // {DAV:}propstat - } if ($empty) { /* @@ -149,12 +143,10 @@ class Response implements Element { * no properties. */ $writer->writeElement('{DAV:}propstat', [ - '{DAV:}prop' => [], - '{DAV:}status' => 'HTTP/1.1 418 ' . \Sabre\HTTP\Response::$statusCodes[418] + '{DAV:}prop' => [], + '{DAV:}status' => 'HTTP/1.1 418 '.\Sabre\HTTP\Response::$statusCodes[418], ]); - } - } /** @@ -176,10 +168,11 @@ class Response implements Element { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $reader->pushContext(); $reader->elementMap['{DAV:}propstat'] = 'Sabre\\Xml\\Element\\KeyValue'; @@ -192,16 +185,16 @@ class Response implements Element { // called. But we don't want this, because a singular element without // child-elements implies 'no value' in {DAV:}prop, so we want to skip // deserializers and just set null for those. - $reader->elementMap['{DAV:}prop'] = function(Reader $reader) { - + $reader->elementMap['{DAV:}prop'] = function (Reader $reader) { if ($reader->isEmptyElement) { $reader->next(); + return []; } $values = []; $reader->read(); do { - if ($reader->nodeType === Reader::ELEMENT) { + if (Reader::ELEMENT === $reader->nodeType) { $clark = $reader->getClark(); if ($reader->isEmptyElement) { @@ -213,10 +206,10 @@ class Response implements Element { } else { $reader->read(); } - } while ($reader->nodeType !== Reader::END_ELEMENT); + } while (Reader::END_ELEMENT !== $reader->nodeType); $reader->read(); - return $values; + return $values; }; $elems = $reader->parseInnerTree(); $reader->popContext(); @@ -226,28 +219,24 @@ class Response implements Element { $statusCode = null; foreach ($elems as $elem) { - switch ($elem['name']) { - - case '{DAV:}href' : + case '{DAV:}href': $href = $elem['value']; break; - case '{DAV:}propstat' : + case '{DAV:}propstat': $status = $elem['value']['{DAV:}status']; - list(, $status, ) = explode(' ', $status, 3); + list(, $status) = explode(' ', $status, 3); $properties = isset($elem['value']['{DAV:}prop']) ? $elem['value']['{DAV:}prop'] : []; - if ($properties) $propertyLists[$status] = $properties; + if ($properties) { + $propertyLists[$status] = $properties; + } break; - case '{DAV:}status' : - list(, $statusCode, ) = explode(' ', $elem['value'], 3); + case '{DAV:}status': + list(, $statusCode) = explode(' ', $elem['value'], 3); break; - } - } return new self($href, $propertyLists, $statusCode); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Sharee.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Sharee.php index e187bf11cb0..e0db3bf3725 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Sharee.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Element/Sharee.php @@ -1,5 +1,7 @@ $v) { - if (property_exists($this, $k)) { $this->$k = $v; } else { - throw new \InvalidArgumentException('Unknown property: ' . $k); + throw new \InvalidArgumentException('Unknown property: '.$k); } - } - } /** @@ -116,31 +115,28 @@ class Sharee implements Element { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - - + public function xmlSerialize(Writer $writer) + { $writer->write([ new Href($this->href), - '{DAV:}prop' => $this->properties, + '{DAV:}prop' => $this->properties, '{DAV:}share-access' => new ShareAccess($this->access), ]); switch ($this->inviteStatus) { - case Plugin::INVITE_NORESPONSE : + case Plugin::INVITE_NORESPONSE: $writer->writeElement('{DAV:}invite-noresponse'); break; - case Plugin::INVITE_ACCEPTED : + case Plugin::INVITE_ACCEPTED: $writer->writeElement('{DAV:}invite-accepted'); break; - case Plugin::INVITE_DECLINED : + case Plugin::INVITE_DECLINED: $writer->writeElement('{DAV:}invite-declined'); break; - case Plugin::INVITE_INVALID : + case Plugin::INVITE_INVALID: $writer->writeElement('{DAV:}invite-invalid'); break; } - } /** @@ -162,10 +158,11 @@ class Sharee implements Element { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { // Temporarily override configuration $reader->pushContext(); $reader->elementMap['{DAV:}share-access'] = 'Sabre\DAV\Xml\Property\ShareAccess'; @@ -192,8 +189,7 @@ class Sharee implements Element { throw new BadRequest('Every {DAV:}sharee must have a {DAV:}share-access child element'); } $sharee->access = $elems['share-access']->getValue(); + return $sharee; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Complex.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Complex.php index 258806e4a59..99030205488 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Complex.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Complex.php @@ -1,5 +1,7 @@ readInnerXml(); - if ($reader->nodeType === Reader::ELEMENT && $reader->isEmptyElement) { + if (Reader::ELEMENT === $reader->nodeType && $reader->isEmptyElement) { // Easy! $reader->next(); + return null; } // Now we have a copy of the inner xml, we need to traverse it to get @@ -56,21 +60,19 @@ class Complex extends XmlFragment { $text = ''; while (true) { - switch ($reader->nodeType) { - case Reader::ELEMENT : + case Reader::ELEMENT: $nonText = true; $reader->next(); continue 2; - case Reader::TEXT : - case Reader::CDATA : + case Reader::TEXT: + case Reader::CDATA: $text .= $reader->value; break; - case Reader::END_ELEMENT : + case Reader::END_ELEMENT: break 2; } $reader->read(); - } // Make sure we advance the cursor one step further. @@ -78,12 +80,10 @@ class Complex extends XmlFragment { if ($nonText) { $new = new self($xml); + return $new; } else { return $text; } - } - - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php index 101a0f0c91a..05a00c5e560 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php @@ -1,5 +1,7 @@ time = clone $time; } else { - $this->time = new DateTime('@' . $time); + $this->time = new DateTime('@'.$time); } // Setting timezone to UTC $this->time->setTimezone(new DateTimeZone('UTC')); - } /** - * getTime + * getTime. * * @return DateTime */ - function getTime() { - + public function getTime() + { return $this->time; - } /** @@ -70,14 +70,12 @@ class GetLastModified implements Element { * responsible for closing them. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { $writer->write( - HTTP\Util::toHTTPDate($this->time) + HTTP\toDate($this->time) ); - } /** @@ -99,12 +97,12 @@ class GetLastModified implements Element { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { return new self(new DateTime($reader->parseInnerTree())); - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Href.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Href.php index 6c4f11b87ad..c479c1602b7 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Href.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Href.php @@ -1,5 +1,7 @@ hrefs = $hrefs; - } /** * Returns the first Href. * - * @return string + * @return string|null */ - function getHref() { - - return $this->hrefs[0]; - + public function getHref() + { + return $this->hrefs[0] ?? null; } /** - * Returns the hrefs as an array + * Returns the hrefs as an array. * * @return array */ - function getHrefs() { - + public function getHrefs() + { return $this->hrefs; - } /** @@ -90,15 +89,13 @@ class Href implements Element, HtmlOutput { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->getHrefs() as $href) { $href = Uri\resolve($writer->contextUri, $href); $writer->writeElement('{DAV:}href', $href); } - } /** @@ -113,16 +110,17 @@ class Href implements Element, HtmlOutput { * be used to construct local links. * * @param HtmlOutputHelper $html + * * @return string */ - function toHtml(HtmlOutputHelper $html) { - + public function toHtml(HtmlOutputHelper $html) + { $links = []; foreach ($this->getHrefs() as $href) { $links[] = $html->link($href); } - return implode('
', $links); + return implode('
', $links); } /** @@ -144,22 +142,21 @@ class Href implements Element, HtmlOutput { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $hrefs = []; - foreach ((array)$reader->parseInnerTree() as $elem) { - if ($elem['name'] !== '{DAV:}href') + foreach ((array) $reader->parseInnerTree() as $elem) { + if ('{DAV:}href' !== $elem['name']) { continue; + } $hrefs[] = $elem['value']; - } if ($hrefs) { - return new self($hrefs, false); + return new self($hrefs); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Invite.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Invite.php index 6adad365040..b5e2dae46d6 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Invite.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/Invite.php @@ -1,5 +1,7 @@ sharees = $sharees; - } /** @@ -57,14 +58,11 @@ class Invite implements XmlSerializable { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->sharees as $sharee) { $writer->writeElement('{DAV:}sharee', $sharee); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/LocalHref.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/LocalHref.php index 00d2fa708d1..cb794974f1b 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/LocalHref.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/LocalHref.php @@ -1,11 +1,13 @@ locks = $locks; - } /** @@ -63,16 +64,14 @@ class LockDiscovery implements XmlSerializable { * responsible for closing them. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->locks as $lock) { - $writer->startElement('{DAV:}activelock'); $writer->startElement('{DAV:}lockscope'); - if ($lock->scope === LockInfo::SHARED) { + if (LockInfo::SHARED === $lock->scope) { $writer->writeElement('{DAV:}shared'); } else { $writer->writeElement('{DAV:}exclusive'); @@ -86,21 +85,23 @@ class LockDiscovery implements XmlSerializable { if (!self::$hideLockRoot) { $writer->startElement('{DAV:}lockroot'); - $writer->writeElement('{DAV:}href', $writer->contextUri . $lock->uri); + $writer->writeElement('{DAV:}href', $writer->contextUri.$lock->uri); $writer->endElement(); // {DAV:}lockroot } - $writer->writeElement('{DAV:}depth', ($lock->depth == DAV\Server::DEPTH_INFINITY ? 'infinity' : $lock->depth)); - $writer->writeElement('{DAV:}timeout', 'Second-' . $lock->timeout); + $writer->writeElement('{DAV:}depth', (DAV\Server::DEPTH_INFINITY == $lock->depth ? 'infinity' : $lock->depth)); + $writer->writeElement('{DAV:}timeout', (LockInfo::TIMEOUT_INFINITE === $lock->timeout ? 'Infinite' : 'Second-'.$lock->timeout)); - $writer->startElement('{DAV:}locktoken'); - $writer->writeElement('{DAV:}href', 'opaquelocktoken:' . $lock->token); - $writer->endElement(); // {DAV:}locktoken + // optional according to https://tools.ietf.org/html/rfc4918#section-6.5 + if (null !== $lock->token && '' !== $lock->token) { + $writer->startElement('{DAV:}locktoken'); + $writer->writeElement('{DAV:}href', 'opaquelocktoken:'.$lock->token); + $writer->endElement(); // {DAV:}locktoken + } - $writer->writeElement('{DAV:}owner', new XmlFragment($lock->owner)); + if ($lock->owner) { + $writer->writeElement('{DAV:}owner', new XmlFragment($lock->owner)); + } $writer->endElement(); // {DAV:}activelock - } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ResourceType.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ResourceType.php index ce640ff32db..ce07d438258 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ResourceType.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ResourceType.php @@ -1,5 +1,7 @@ value; - } /** - * Checks if the principal contains a certain value + * Checks if the principal contains a certain value. * * @param string $type + * * @return bool */ - function is($type) { - + public function is($type) + { return in_array($type, $this->value); - } /** - * Adds a resourcetype value to this property + * Adds a resourcetype value to this property. * * @param string $type - * @return void */ - function add($type) { - + public function add($type) + { $this->value[] = $type; $this->value = array_unique($this->value); - } /** @@ -93,13 +91,13 @@ class ResourceType extends Element\Elements implements HtmlOutput { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { return new self(parent::xmlDeserialize($reader)); - } /** @@ -114,15 +112,14 @@ class ResourceType extends Element\Elements implements HtmlOutput { * be used to construct local links. * * @param HtmlOutputHelper $html + * * @return string */ - function toHtml(HtmlOutputHelper $html) { - + public function toHtml(HtmlOutputHelper $html) + { return implode( ', ', array_map([$html, 'xmlName'], $this->getValue()) ); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php index 939062f76fd..95175053ac9 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/ShareAccess.php @@ -1,5 +1,7 @@ value = $shareAccess; - } /** @@ -50,10 +51,9 @@ class ShareAccess implements Element { * * @return int */ - function getValue() { - + public function getValue() + { return $this->value; - } /** @@ -73,30 +73,26 @@ class ShareAccess implements Element { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { switch ($this->value) { - - case SharingPlugin::ACCESS_NOTSHARED : + case SharingPlugin::ACCESS_NOTSHARED: $writer->writeElement('{DAV:}not-shared'); break; - case SharingPlugin::ACCESS_SHAREDOWNER : + case SharingPlugin::ACCESS_SHAREDOWNER: $writer->writeElement('{DAV:}shared-owner'); break; - case SharingPlugin::ACCESS_READ : + case SharingPlugin::ACCESS_READ: $writer->writeElement('{DAV:}read'); break; - case SharingPlugin::ACCESS_READWRITE : + case SharingPlugin::ACCESS_READWRITE: $writer->writeElement('{DAV:}read-write'); break; - case SharingPlugin::ACCESS_NOACCESS : + case SharingPlugin::ACCESS_NOACCESS: $writer->writeElement('{DAV:}no-access'); break; - } - } /** @@ -118,26 +114,26 @@ class ShareAccess implements Element { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $elems = $reader->parseInnerTree(); foreach ($elems as $elem) { switch ($elem['name']) { - case '{DAV:}not-shared' : + case '{DAV:}not-shared': return new self(SharingPlugin::ACCESS_NOTSHARED); - case '{DAV:}shared-owner' : + case '{DAV:}shared-owner': return new self(SharingPlugin::ACCESS_SHAREDOWNER); - case '{DAV:}read' : + case '{DAV:}read': return new self(SharingPlugin::ACCESS_READ); - case '{DAV:}read-write' : + case '{DAV:}read-write': return new self(SharingPlugin::ACCESS_READWRITE); - case '{DAV:}no-access' : + case '{DAV:}no-access': return new self(SharingPlugin::ACCESS_NOACCESS); } } throw new BadRequest('Invalid value for {DAV:}share-access element'); - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php index 677fdde4bdf..26e7d646efb 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedLock.php @@ -1,5 +1,7 @@ writeElement('{DAV:}lockentry', [ '{DAV:}lockscope' => ['{DAV:}exclusive' => null], - '{DAV:}locktype' => ['{DAV:}write' => null], + '{DAV:}locktype' => ['{DAV:}write' => null], ]); $writer->writeElement('{DAV:}lockentry', [ '{DAV:}lockscope' => ['{DAV:}shared' => null], - '{DAV:}locktype' => ['{DAV:}write' => null], + '{DAV:}locktype' => ['{DAV:}write' => null], ]); - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php index 1a3878ef71f..06ab28c942d 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedMethodSet.php @@ -1,5 +1,7 @@ methods = $methods; - } /** @@ -45,25 +46,24 @@ class SupportedMethodSet implements XmlSerializable, HtmlOutput { * * @return string[] */ - function getValue() { - + public function getValue() + { return $this->methods; - } /** * Returns true or false if the property contains a specific method. * * @param string $methodName + * * @return bool */ - function has($methodName) { - + public function has($methodName) + { return in_array( $methodName, $this->methods ); - } /** @@ -83,16 +83,14 @@ class SupportedMethodSet implements XmlSerializable, HtmlOutput { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->getValue() as $val) { $writer->startElement('{DAV:}supported-method'); $writer->writeAttribute('name', $val); $writer->endElement(); } - } /** @@ -107,15 +105,14 @@ class SupportedMethodSet implements XmlSerializable, HtmlOutput { * be used to construct local links. * * @param HtmlOutputHelper $html + * * @return string */ - function toHtml(HtmlOutputHelper $html) { - + public function toHtml(HtmlOutputHelper $html) + { return implode( ', ', array_map([$html, 'h'], $this->getValue()) ); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php index 96383ec96c8..4c25d23d976 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Property/SupportedReportSet.php @@ -1,5 +1,7 @@ addReport($reports); - + } } /** - * Adds a report to this property + * 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) { - - $report = (array)$report; + public function addReport($report) + { + $report = (array) $report; foreach ($report as $r) { - - if (!preg_match('/^{([^}]*)}(.*)$/', $r)) + if (!preg_match('/^{([^}]*)}(.*)$/', $r)) { throw new DAV\Exception('Reportname must be in clark-notation'); - + } $this->reports[] = $r; - } - } /** - * Returns the list of supported reports + * Returns the list of supported reports. * * @return string[] */ - function getValue() { - + public function getValue() + { return $this->reports; - } /** * Returns true or false if the property contains a specific report. * * @param string $reportName + * * @return bool */ - function has($reportName) { - + public function has($reportName) + { return in_array( $reportName, $this->reports ); - } /** @@ -114,10 +111,9 @@ class SupportedReportSet implements XmlSerializable, HtmlOutput { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->getValue() as $val) { $writer->startElement('{DAV:}supported-report'); $writer->startElement('{DAV:}report'); @@ -125,7 +121,6 @@ class SupportedReportSet implements XmlSerializable, HtmlOutput { $writer->endElement(); $writer->endElement(); } - } /** @@ -140,15 +135,14 @@ class SupportedReportSet implements XmlSerializable, HtmlOutput { * be used to construct local links. * * @param HtmlOutputHelper $html + * * @return string */ - function toHtml(HtmlOutputHelper $html) { - + public function toHtml(HtmlOutputHelper $html) + { return implode( ', ', array_map([$html, 'xmlName'], $this->getValue()) ); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/Lock.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/Lock.php index c315a9a4502..8d934816253 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/Lock.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/Lock.php @@ -1,5 +1,7 @@ pushContext(); $reader->elementMap['{DAV:}owner'] = 'Sabre\\Xml\\Element\\XmlFragment'; @@ -71,11 +75,12 @@ class Lock implements XmlDeserializable { if (isset($values['{DAV:}lockscope'])) { foreach ($values['{DAV:}lockscope'] as $elem) { - if ($elem['name'] === '{DAV:}exclusive') $new->scope = LockInfo::EXCLUSIVE; + if ('{DAV:}exclusive' === $elem['name']) { + $new->scope = LockInfo::EXCLUSIVE; + } } } + return $new; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/MkCol.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/MkCol.php index 9490bf58cf2..7713646eaf2 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/MkCol.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/MkCol.php @@ -1,5 +1,7 @@ properties; - } /** @@ -56,10 +57,11 @@ class MkCol implements XmlDeserializable { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $self = new self(); $elementMap = $reader->elementMap; @@ -70,13 +72,11 @@ class MkCol implements XmlDeserializable { $elems = $reader->parseInnerTree($elementMap); foreach ($elems as $elem) { - if ($elem['name'] === '{DAV:}set') { + if ('{DAV:}set' === $elem['name']) { $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']); } } return $self; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropFind.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropFind.php index f1b5b6fdc6f..b4cce423e32 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropFind.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropFind.php @@ -1,5 +1,7 @@ pushContext(); $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Element\Elements'; foreach (KeyValue::xmlDeserialize($reader) as $k => $v) { - switch ($k) { - case '{DAV:}prop' : + case '{DAV:}prop': $self->properties = $v; break; - case '{DAV:}allprop' : + case '{DAV:}allprop': $self->allProp = true; - } - } $reader->popContext(); return $self; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropPatch.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropPatch.php index 821b9e047d4..55e154ec766 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropPatch.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/PropPatch.php @@ -1,5 +1,7 @@ properties as $propertyName => $propertyValue) { - if (is_null($propertyValue)) { - $writer->startElement("{DAV:}remove"); + $writer->startElement('{DAV:}remove'); $writer->write(['{DAV:}prop' => [$propertyName => $propertyValue]]); $writer->endElement(); } else { - $writer->startElement("{DAV:}set"); + $writer->startElement('{DAV:}set'); $writer->write(['{DAV:}prop' => [$propertyName => $propertyValue]]); $writer->endElement(); } - } - } /** @@ -84,10 +82,11 @@ class PropPatch implements Element { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $self = new self(); $elementMap = $reader->elementMap; @@ -98,21 +97,17 @@ class PropPatch implements Element { $elems = $reader->parseInnerTree($elementMap); foreach ($elems as $elem) { - if ($elem['name'] === '{DAV:}set') { + if ('{DAV:}set' === $elem['name']) { $self->properties = array_merge($self->properties, $elem['value']['{DAV:}prop']); } - if ($elem['name'] === '{DAV:}remove') { - + if ('{DAV:}remove' === $elem['name']) { // Ensuring there are no values. foreach ($elem['value']['{DAV:}prop'] as $remove => $value) { $self->properties[$remove] = null; } - } } return $self; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/ShareResource.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/ShareResource.php index 526a4eb6ff6..e1985b8c351 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/ShareResource.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/ShareResource.php @@ -1,5 +1,7 @@ sharees = $sharees; - } /** @@ -56,26 +57,26 @@ class ShareResource implements XmlDeserializable { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $elems = $reader->parseInnerTree([ - '{DAV:}sharee' => 'Sabre\DAV\Xml\Element\Sharee', + '{DAV:}sharee' => 'Sabre\DAV\Xml\Element\Sharee', '{DAV:}share-access' => 'Sabre\DAV\Xml\Property\ShareAccess', - '{DAV:}prop' => 'Sabre\Xml\Deserializer\keyValue', + '{DAV:}prop' => 'Sabre\Xml\Deserializer\keyValue', ]); $sharees = []; foreach ($elems as $elem) { - if ($elem['name'] !== '{DAV:}sharee') continue; + if ('{DAV:}sharee' !== $elem['name']) { + continue; + } $sharees[] = $elem['value']; - } return new self($sharees); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php index 830293a0178..acf0039ce22 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php @@ -1,5 +1,7 @@ pushContext(); @@ -87,36 +90,31 @@ class SyncCollectionReport implements XmlDeserializable { foreach ($required as $elem) { if (!array_key_exists($elem, $elems)) { - throw new BadRequest('The ' . $elem . ' element in the {DAV:}sync-collection report is required'); + throw new BadRequest('The '.$elem.' element in the {DAV:}sync-collection report is required'); } } - $self->properties = $elems['{DAV:}prop']; $self->syncToken = $elems['{DAV:}sync-token']; if (isset($elems['{DAV:}limit'])) { $nresults = null; foreach ($elems['{DAV:}limit'] as $child) { - if ($child['name'] === '{DAV:}nresults') { - $nresults = (int)$child['value']; + if ('{DAV:}nresults' === $child['name']) { + $nresults = (int) $child['value']; } } $self->limit = $nresults; } if (isset($elems['{DAV:}sync-level'])) { - $value = $elems['{DAV:}sync-level']; - if ($value === 'infinity') { + if ('infinity' === $value) { $value = \Sabre\DAV\Server::DEPTH_INFINITY; } $self->syncLevel = $value; - } return $self; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php index cf5a0453b4a..423d0b56908 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php @@ -1,5 +1,7 @@ responses = $responses; $this->syncToken = $syncToken; - } /** @@ -53,10 +54,9 @@ class MultiStatus implements Element { * * @return \Sabre\DAV\Xml\Element\Response[] */ - function getResponses() { - + public function getResponses() + { return $this->responses; - } /** @@ -64,10 +64,9 @@ class MultiStatus implements Element { * * @return string|null */ - function getSyncToken() { - + public function getSyncToken() + { return $this->syncToken; - } /** @@ -83,17 +82,15 @@ class MultiStatus implements Element { * responsible for closing them. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->getResponses() as $response) { $writer->writeElement('{DAV:}response', $response); } if ($syncToken = $this->getSyncToken()) { $writer->writeElement('{DAV:}sync-token', $syncToken); } - } /** @@ -115,10 +112,11 @@ class MultiStatus implements Element { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $elementMap = $reader->elementMap; $elementMap['{DAV:}prop'] = 'Sabre\\DAV\\Xml\\Element\\Prop'; $elements = $reader->parseInnerTree($elementMap); @@ -126,17 +124,17 @@ class MultiStatus implements Element { $responses = []; $syncToken = null; - if ($elements) foreach ($elements as $elem) { - if ($elem['name'] === '{DAV:}response') { - $responses[] = $elem['value']; - } - if ($elem['name'] === '{DAV:}sync-token') { - $syncToken = $elem['value']; + if ($elements) { + foreach ($elements as $elem) { + if ('{DAV:}response' === $elem['name']) { + $responses[] = $elem['value']; + } + if ('{DAV:}sync-token' === $elem['name']) { + $syncToken = $elem['value']; + } } } return new self($responses, $syncToken); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Service.php b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Service.php index f41ed984ad9..4406b022d0f 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Service.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAV/Xml/Service.php @@ -1,16 +1,18 @@ 'Sabre\\DAV\\Xml\\Response\\MultiStatus', - '{DAV:}response' => 'Sabre\\DAV\\Xml\\Element\\Response', + '{DAV:}response' => 'Sabre\\DAV\\Xml\\Element\\Response', // Requests - '{DAV:}propfind' => 'Sabre\\DAV\\Xml\\Request\\PropFind', + '{DAV:}propfind' => 'Sabre\\DAV\\Xml\\Request\\PropFind', '{DAV:}propertyupdate' => 'Sabre\\DAV\\Xml\\Request\\PropPatch', - '{DAV:}mkcol' => 'Sabre\\DAV\\Xml\\Request\\MkCol', + '{DAV:}mkcol' => 'Sabre\\DAV\\Xml\\Request\\MkCol', // Properties '{DAV:}resourcetype' => 'Sabre\\DAV\\Xml\\Property\\ResourceType', - ]; /** @@ -40,8 +41,7 @@ class Service extends \Sabre\Xml\Service { * @var array */ public $namespaceMap = [ - 'DAV:' => 'd', + 'DAV:' => 'd', 'http://sabredav.org/ns' => 's', ]; - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/ACLTrait.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/ACLTrait.php index 602654a2ed3..d9bf4e1e085 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/ACLTrait.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/ACLTrait.php @@ -1,5 +1,7 @@ '{DAV:}all', 'principal' => '{DAV:}owner', 'protected' => true, - ] + ], ]; - } /** - * Updates the ACL + * 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) { - + public function setACL(array $acl) + { throw new \Sabre\DAV\Exception\Forbidden('Setting ACL is not supported on this node'); } @@ -91,10 +89,8 @@ trait ACLTrait { * * @return array|null */ - function getSupportedPrivilegeSet() { - + public function getSupportedPrivilegeSet() + { return null; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php index 9d2026380c5..1160dd7acb3 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php @@ -1,12 +1,14 @@ principalPrefix = $principalPrefix; $this->principalBackend = $principalBackend; - } /** @@ -68,56 +69,58 @@ abstract class AbstractPrincipalCollection extends DAV\Collection implements IPr * supplied by the authentication backend. * * @param array $principalInfo - * @return IPrincipal + * + * @return DAV\INode */ - abstract function getChildForPrincipal(array $principalInfo); + abstract public function getChildForPrincipal(array $principalInfo); /** * Returns the name of this collection. * * @return string */ - function getName() { + public function getName() + { + list(, $name) = Uri\split($this->principalPrefix); - list(, $name) = URLUtil::splitPath($this->principalPrefix); return $name; - } /** - * Return the list of users + * Return the list of users. * * @return array */ - function getChildren() { - - if ($this->disableListing) + public 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; + return $children; } /** * Returns a child object, by its name. * * @param string $name + * * @throws DAV\Exception\NotFound + * * @return DAV\INode */ - function getChild($name) { + public function getChild($name) + { + $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix.'/'.$name); + if (!$principalInfo) { + throw new DAV\Exception\NotFound('Principal with name '.$name.' not found'); + } - $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name); - if (!$principalInfo) throw new DAV\Exception\NotFound('Principal with name ' . $name . ' not found'); return $this->getChildForPrincipal($principalInfo); - } /** @@ -139,21 +142,21 @@ abstract class AbstractPrincipalCollection extends DAV\Collection implements IPr * This method should simply return a list of 'child names', which may be * used to call $this->getChild in the future. * - * @param array $searchProperties + * @param array $searchProperties * @param string $test + * * @return array */ - function searchPrincipals(array $searchProperties, $test = 'allof') { - + public function searchPrincipals(array $searchProperties, $test = 'allof') + { $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties, $test); $r = []; foreach ($result as $row) { - list(, $r[]) = URLUtil::splitPath($row); + list(, $r[]) = Uri\split($row); } return $r; - } /** @@ -170,12 +173,11 @@ abstract class AbstractPrincipalCollection extends DAV\Collection implements IPr * principal was not found or you refuse to find it. * * @param string $uri + * * @return string */ - function findByUri($uri) { - + public function findByUri($uri) + { return $this->principalBackend->findByUri($uri, $this->principalPrefix); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/AceConflict.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/AceConflict.php index 22450b4a681..7756d472858 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/AceConflict.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/AceConflict.php @@ -1,5 +1,7 @@ ownerDocument; $np = $doc->createElementNS('DAV:', 'd:no-ace-conflict'); $errorNode->appendChild($np); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php index 5624fd22f23..19e0dac9d11 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php @@ -1,11 +1,13 @@ uri = $uri; $this->privileges = $privileges; - parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"'); - + parent::__construct('User did not have the required privileges ('.implode(',', $privileges).') for path "'.$uri.'"'); } /** @@ -50,33 +51,27 @@ class NeedPrivileges extends DAV\Exception\Forbidden { * * This method adds the {DAV:}need-privileges element as defined in rfc3744 * - * @param DAV\Server $server + * @param DAV\Server $server * @param \DOMElement $errorNode - * @return void */ - function serialize(DAV\Server $server, \DOMElement $errorNode) { - + public 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)); + $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])); - - + $priv->appendChild($doc->createElementNS($privilegeParts[1], 'd:'.$privilegeParts[2])); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NoAbstract.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NoAbstract.php index a2363b174bf..d90b01ffd43 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NoAbstract.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NoAbstract.php @@ -1,5 +1,7 @@ ownerDocument; $np = $doc->createElementNS('DAV:', 'd:no-abstract'); $errorNode->appendChild($np); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php index d7ae188ae7e..b005c55d8b0 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php @@ -1,5 +1,7 @@ ownerDocument; $np = $doc->createElementNS('DAV:', 'd:recognized-principal'); $errorNode->appendChild($np); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php index 73b81190dd2..dda2e62815e 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php @@ -1,5 +1,7 @@ ownerDocument; $np = $doc->createElementNS('DAV:', 'd:not-supported-privilege'); $errorNode->appendChild($np); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/Collection.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/Collection.php index b4fe7a1b0be..85b04e2bdf0 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/Collection.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/Collection.php @@ -1,5 +1,7 @@ acl = $acl; $this->owner = $owner; - } /** - * Returns a specific child node, referenced by its name + * 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 NotFound + * * @return \Sabre\DAV\INode */ - function getChild($name) { - - $path = $this->path . '/' . $name; - - if (!file_exists($path)) throw new NotFound('File could not be located'); - if ($name == '.' || $name == '..') throw new Forbidden('Permission denied to . and ..'); - - if (is_dir($path)) { - - return new self($path, $this->acl, $this->owner); - - } else { - - return new File($path, $this->acl, $this->owner); + public function getChild($name) + { + $path = $this->path.'/'.$name; + if (!file_exists($path)) { + throw new NotFound('File could not be located'); + } + if ('.' == $name || '..' == $name) { + throw new Forbidden('Permission denied to . and ..'); + } + if (is_dir($path)) { + return new self($path, $this->acl, $this->owner); + } else { + return new File($path, $this->acl, $this->owner); } - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->owner; - } /** @@ -102,10 +102,8 @@ class Collection extends BaseCollection implements IACL { * * @return array */ - function getACL() { - + public function getACL() + { return $this->acl; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/File.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/File.php index aaf2ae148a7..5506aa2c15e 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/File.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/File.php @@ -1,5 +1,7 @@ acl = $acl; $this->owner = $owner; - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->owner; - } /** @@ -71,10 +71,8 @@ class File extends BaseFile implements IACL { * * @return array */ - function getACL() { - + public function getACL() + { return $this->acl; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/HomeCollection.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/HomeCollection.php index 201235e5a0f..c896d8e25ea 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/HomeCollection.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/FS/HomeCollection.php @@ -1,5 +1,7 @@ storagePath = $storagePath; - } /** @@ -58,10 +59,9 @@ class HomeCollection extends AbstractPrincipalCollection implements IACL { * * @return string */ - function getName() { - + public function getName() + { return $this->collectionName; - } /** @@ -72,10 +72,11 @@ class HomeCollection extends AbstractPrincipalCollection implements IACL { * supplied by the authentication backend. * * @param array $principalInfo + * * @return \Sabre\DAV\INode */ - function getChildForPrincipal(array $principalInfo) { - + public function getChildForPrincipal(array $principalInfo) + { $owner = $principalInfo['uri']; $acl = [ [ @@ -87,20 +88,19 @@ class HomeCollection extends AbstractPrincipalCollection implements IACL { list(, $principalBaseName) = Uri\split($owner); - $path = $this->storagePath . '/' . $principalBaseName; + $path = $this->storagePath.'/'.$principalBaseName; if (!is_dir($path)) { mkdir($path, 0777, true); } + return new Collection( $path, $acl, $owner ); - } - /** * Returns a list of ACE's for this node. * @@ -113,16 +113,14 @@ class HomeCollection extends AbstractPrincipalCollection implements IACL { * * @return array */ - function getACL() { - + public function getACL() + { return [ [ 'principal' => '{DAV:}authenticated', 'privilege' => '{DAV:}read', 'protected' => true, - ] + ], ]; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/IACL.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/IACL.php index f7a138665a5..d19a075be74 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/IACL.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/IACL.php @@ -1,11 +1,13 @@ getChild in the future. * - * @param array $searchProperties + * @param array $searchProperties * @param string $test + * * @return array */ - function searchPrincipals(array $searchProperties, $test = 'allof'); + public function searchPrincipals(array $searchProperties, $test = 'allof'); /** * Finds a principal by its URI. @@ -55,8 +58,8 @@ interface IPrincipalCollection extends DAV\ICollection { * principal was not found or you refuse to find it. * * @param string $uri + * * @return string */ - function findByUri($uri); - + public function findByUri($uri); } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Plugin.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Plugin.php index a2aa118d70b..c1ea6027cad 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Plugin.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Plugin.php @@ -1,5 +1,7 @@ 'Display name', + '{DAV:}displayname' => 'Display name', '{http://sabredav.org/ns}email-address' => 'Email address', ]; @@ -120,22 +122,21 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getFeatures() { - + public function getFeatures() + { return ['access-control', 'calendarserver-principal-property-search']; - } /** - * Returns a list of available methods for a given url + * Returns a list of available methods for a given url. * * @param string $uri + * * @return array */ - function getMethods($uri) { - + public function getMethods($uri) + { return ['ACL']; - } /** @@ -146,10 +147,9 @@ class Plugin extends DAV\ServerPlugin { * * @return string */ - function getPluginName() { - + public function getPluginName() + { return 'acl'; - } /** @@ -160,20 +160,19 @@ class Plugin extends DAV\ServerPlugin { * implement them * * @param string $uri + * * @return array */ - function getSupportedReportSet($uri) { - + public function getSupportedReportSet($uri) + { return [ '{DAV:}expand-property', '{DAV:}principal-match', '{DAV:}principal-property-search', '{DAV:}principal-search-property-set', ]; - } - /** * Checks if the current user has the specified privilege(s). * @@ -181,27 +180,29 @@ class Plugin extends DAV\ServerPlugin { * This method will throw an exception if the privilege is not available * and return true otherwise. * - * @param string $uri + * @param string $uri * @param array|string $privileges - * @param int $recursion - * @param bool $throwExceptions if set to false, this method won't throw exceptions. + * @param int $recursion + * @param bool $throwExceptions if set to false, this method won't throw exceptions + * * @throws NeedPrivileges * @throws NotAuthenticated + * * @return bool */ - function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) { - - if (!is_array($privileges)) $privileges = [$privileges]; + public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) + { + if (!is_array($privileges)) { + $privileges = [$privileges]; + } $acl = $this->getCurrentUserPrivilegeSet($uri); $failed = []; foreach ($privileges as $priv) { - if (!in_array($priv, $acl)) { $failed[] = $priv; } - } if ($failed) { @@ -213,17 +214,16 @@ class Plugin extends DAV\ServerPlugin { $this->server->httpRequest, $this->server->httpResponse ); - throw new notAuthenticated(implode(', ', $reasons) . '. Login was needed for privilege: ' . implode(', ', $failed) . ' on ' . $uri); + throw new notAuthenticated(implode(', ', $reasons).'. Login was needed for privilege: '.implode(', ', $failed).' on '.$uri); } if ($throwExceptions) { - throw new NeedPrivileges($uri, $failed); } else { return false; } } - return true; + return true; } /** @@ -234,35 +234,35 @@ class Plugin extends DAV\ServerPlugin { * * @return string|null */ - function getCurrentUserPrincipal() { - + public function getCurrentUserPrincipal() + { /** @var $authPlugin \Sabre\DAV\Auth\Plugin */ $authPlugin = $this->server->getPlugin('auth'); if (!$authPlugin) { return null; } + return $authPlugin->getCurrentPrincipal(); - } - /** * Returns a list of principals that's associated to the current * user, either directly or through group membership. * * @return array */ - function getCurrentUserPrincipals() { - + public function getCurrentUserPrincipals() + { $currentUser = $this->getCurrentUserPrincipal(); - if (is_null($currentUser)) return []; + if (is_null($currentUser)) { + return []; + } return array_merge( [$currentUser], $this->getPrincipalMembership($currentUser) ); - } /** @@ -271,12 +271,10 @@ class Plugin extends DAV\ServerPlugin { * These rules are used for all nodes that don't implement the IACL interface. * * @param array $acl - * @return void */ - function setDefaultAcl(array $acl) { - + public function setDefaultAcl(array $acl) + { $this->defaultAcl = $acl; - } /** @@ -286,10 +284,9 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getDefaultAcl() { - + public function getDefaultAcl() + { return $this->defaultAcl; - } /** @@ -317,15 +314,15 @@ class Plugin extends DAV\ServerPlugin { */ protected $principalMembershipCache = []; - /** * Returns all the principal groups the specified principal is a member of. * * @param string $mainPrincipal + * * @return array */ - function getPrincipalMembership($mainPrincipal) { - + public function getPrincipalMembership($mainPrincipal) + { // First check our cache if (isset($this->principalMembershipCache[$mainPrincipal])) { return $this->principalMembershipCache[$mainPrincipal]; @@ -335,31 +332,23 @@ class Plugin extends DAV\ServerPlugin { $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; - } /** @@ -381,24 +370,27 @@ class Plugin extends DAV\ServerPlugin { * * @param string $checkPrincipal * @param string $currentPrincipal + * * @return bool */ - function principalMatchesPrincipal($checkPrincipal, $currentPrincipal = null) { - + public function principalMatchesPrincipal($checkPrincipal, $currentPrincipal = null) + { if (is_null($currentPrincipal)) { $currentPrincipal = $this->getCurrentUserPrincipal(); } if ($currentPrincipal === $checkPrincipal) { return true; } + if (is_null($currentPrincipal)) { + return false; + } + return in_array( $checkPrincipal, $this->getPrincipalMembership($currentPrincipal) ); - } - /** * Returns a tree of supported privileges for a resource. * @@ -436,10 +428,11 @@ class Plugin extends DAV\ServerPlugin { * ] * * @param string|INode $node + * * @return array */ - function getSupportedPrivilegeSet($node) { - + public function getSupportedPrivilegeSet($node) + { if (is_string($node)) { $node = $this->server->tree->getNodeForPath($node); } @@ -450,35 +443,34 @@ class Plugin extends DAV\ServerPlugin { } if (is_null($supportedPrivileges)) { - // Default $supportedPrivileges = [ '{DAV:}read' => [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [ '{DAV:}read-acl' => [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ], '{DAV:}read-current-user-privilege-set' => [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ], ], ], '{DAV:}write' => [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [ '{DAV:}write-properties' => [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ], '{DAV:}write-content' => [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ], '{DAV:}unlock' => [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ], ], @@ -486,21 +478,20 @@ class Plugin extends DAV\ServerPlugin { ]; if ($node instanceof DAV\ICollection) { $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}bind'] = [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ]; $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}unbind'] = [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ]; } if ($node instanceof IACL) { $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}write-acl'] = [ - 'abstract' => false, + 'abstract' => false, 'aggregates' => [], ]; } - } $this->server->emit( @@ -509,11 +500,10 @@ class Plugin extends DAV\ServerPlugin { ); return $supportedPrivileges; - } /** - * Returns the supported privilege set as a flat list + * Returns the supported privilege set as a flat list. * * This is much easier to parse. * @@ -524,54 +514,44 @@ class Plugin extends DAV\ServerPlugin { * - concrete * * @param string|INode $node + * * @return array */ - final function getFlatPrivilegeSet($node) { - + final public function getFlatPrivilegeSet($node) + { $privs = [ - 'abstract' => false, - 'aggregates' => $this->getSupportedPrivilegeSet($node) + 'abstract' => false, + 'aggregates' => $this->getSupportedPrivilegeSet($node), ]; $fpsTraverse = null; - $fpsTraverse = function($privName, $privInfo, $concrete, &$flat) use (&$fpsTraverse) { - + $fpsTraverse = function ($privName, $privInfo, $concrete, &$flat) use (&$fpsTraverse) { $myPriv = [ - 'privilege' => $privName, - 'abstract' => isset($privInfo['abstract']) && $privInfo['abstract'], + 'privilege' => $privName, + 'abstract' => isset($privInfo['abstract']) && $privInfo['abstract'], 'aggregates' => [], - 'concrete' => isset($privInfo['abstract']) && $privInfo['abstract'] ? $concrete : $privName, + 'concrete' => isset($privInfo['abstract']) && $privInfo['abstract'] ? $concrete : $privName, ]; if (isset($privInfo['aggregates'])) { - foreach ($privInfo['aggregates'] as $subPrivName => $subPrivInfo) { - $myPriv['aggregates'][] = $subPrivName; - } - } $flat[$privName] = $myPriv; if (isset($privInfo['aggregates'])) { - foreach ($privInfo['aggregates'] as $subPrivName => $subPrivInfo) { - $fpsTraverse($subPrivName, $subPrivInfo, $myPriv['concrete'], $flat); - } - } - }; $flat = []; $fpsTraverse('{DAV:}all', $privs, null, $flat); return $flat; - } /** @@ -582,10 +562,11 @@ class Plugin extends DAV\ServerPlugin { * null will be returned if the node doesn't support ACLs. * * @param string|DAV\INode $node + * * @return array */ - function getAcl($node) { - + public function getAcl($node) + { if (is_string($node)) { $node = $this->server->tree->getNodeForPath($node); } @@ -600,8 +581,8 @@ class Plugin extends DAV\ServerPlugin { 'protected' => true, ]; } - return $acl; + return $acl; } /** @@ -613,10 +594,11 @@ class Plugin extends DAV\ServerPlugin { * null will be returned if the node doesn't support ACLs. * * @param string|DAV\INode $node + * * @return array */ - function getCurrentUserPrivilegeSet($node) { - + public function getCurrentUserPrivilegeSet($node) + { if (is_string($node)) { $node = $this->server->tree->getNodeForPath($node); } @@ -625,50 +607,44 @@ class Plugin extends DAV\ServerPlugin { $collected = []; - $isAuthenticated = $this->getCurrentUserPrincipal() !== null; + $isAuthenticated = null !== $this->getCurrentUserPrincipal(); foreach ($acl as $ace) { - $principal = $ace['principal']; switch ($principal) { - - case '{DAV:}owner' : + case '{DAV:}owner': $owner = $node->getOwner(); if ($owner && $this->principalMatchesPrincipal($owner)) { $collected[] = $ace; } break; - // 'all' matches for every user - case '{DAV:}all' : + case '{DAV:}all': $collected[] = $ace; break; - case '{DAV:}authenticated' : + case '{DAV:}authenticated': // Authenticated users only if ($isAuthenticated) { $collected[] = $ace; } break; - case '{DAV:}unauthenticated' : + case '{DAV:}unauthenticated': // Unauthenticated users only if (!$isAuthenticated) { $collected[] = $ace; } break; - default : + default: if ($this->principalMatchesPrincipal($ace['principal'])) { $collected[] = $ace; } break; - } - - } // Now we deduct all aggregated privileges. @@ -676,41 +652,37 @@ class Plugin extends DAV\ServerPlugin { $collected2 = []; while (count($collected)) { - $current = array_pop($collected); $collected2[] = $current['privilege']; if (!isset($flat[$current['privilege']])) { // Ignoring privileges that are not in the supported-privileges list. - $this->server->getLogger()->debug('A node has the "' . $current['privilege'] . '" in its ACL list, but this privilege was not reported in the supportedPrivilegeSet list. This will be ignored.'); + $this->server->getLogger()->debug('A node has the "'.$current['privilege'].'" in its ACL list, but this privilege was not reported in the supportedPrivilegeSet list. This will be ignored.'); continue; } foreach ($flat[$current['privilege']]['aggregates'] as $subPriv) { $collected2[] = $subPriv; $collected[] = $flat[$subPriv]; } - } return array_values(array_unique($collected2)); - } - /** * Returns a principal based on its uri. * * Returns null if the principal could not be found. * * @param string $uri - * @return null|string + * + * @return string|null */ - function getPrincipalByUri($uri) { - + public function getPrincipalByUri($uri) + { $result = null; $collections = $this->principalCollectionSet; foreach ($collections as $collection) { - try { $principalCollection = $this->server->tree->getNodeForPath($collection); } catch (NotFound $e) { @@ -728,36 +700,35 @@ class Plugin extends DAV\ServerPlugin { if ($result) { return $result; } - } - } /** - * Principal property search + * 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. + * @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') { - + public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null, $test = 'allof') + { if (!is_null($collectionUri)) { $uris = [$collectionUri]; } else { @@ -766,7 +737,6 @@ class Plugin extends DAV\ServerPlugin { $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 @@ -776,33 +746,28 @@ class Plugin extends DAV\ServerPlugin { $results = $principalCollection->searchPrincipals($searchProperties, $test); foreach ($results as $result) { - $lookupResults[] = rtrim($uri, '/') . '/' . $result; + $lookupResults[] = rtrim($uri, '/').'/'.$result; } - } $matches = []; foreach ($lookupResults as $lookupResult) { - list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0); - } return $matches; - } /** - * Sets up the plugin + * Sets up the plugin. * * This method is automatically called by the server class. * * @param DAV\Server $server - * @return void */ - function initialize(DAV\Server $server) { - + public function initialize(DAV\Server $server) + { if ($this->allowUnauthenticatedAccess) { $authPlugin = $server->getPlugin('auth'); if (!$authPlugin) { @@ -812,22 +777,22 @@ class Plugin extends DAV\ServerPlugin { } $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']); - $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); - $server->on('getPrincipalByUri', function($principal, &$uri) { - + $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']); + $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); + $server->on('getPrincipalByUri', function ($principal, &$uri) { $uri = $this->getPrincipalByUri($principal); // Break event chain - if ($uri) return false; - + if ($uri) { + return false; + } }); array_push($server->protectedProperties, @@ -858,39 +823,38 @@ class Plugin extends DAV\ServerPlugin { $server->xml->elementMap['{DAV:}principal-property-search'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport'; $server->xml->elementMap['{DAV:}principal-search-property-set'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport'; $server->xml->elementMap['{DAV:}principal-match'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalMatchReport'; - } /* {{{ Event handlers */ /** - * Triggered before any method is handled + * Triggered before any method is handled. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @return void */ - function beforeMethod(RequestInterface $request, ResponseInterface $response) { - + public 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; + if (!$exists) { + return; + } switch ($method) { - - case 'GET' : - case 'HEAD' : - case 'OPTIONS' : + 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 'PUT': + case 'LOCK': // This method requires the write-content priv if the node // already exists, and bind on the parent if the node is being // created. @@ -898,20 +862,20 @@ class Plugin extends DAV\ServerPlugin { $this->checkPrivileges($path, '{DAV:}write-content'); break; - case 'UNLOCK' : + case 'UNLOCK': // Unlock is always allowed at the moment. break; - case 'PROPPATCH' : + case 'PROPPATCH': $this->checkPrivileges($path, '{DAV:}write-properties'); break; - case 'ACL' : + case 'ACL': $this->checkPrivileges($path, '{DAV:}write-acl'); break; - case 'COPY' : - case 'MOVE' : + 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 @@ -925,9 +889,7 @@ class Plugin extends DAV\ServerPlugin { // the sourcenode can be deleted. $this->checkPrivileges($path, '{DAV:}read', self::R_RECURSIVE); break; - } - } /** @@ -937,54 +899,49 @@ class Plugin extends DAV\ServerPlugin { * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE. * * @param string $uri - * @return void */ - function beforeBind($uri) { - + public function beforeBind($uri) + { list($parentUri) = Uri\split($uri); $this->checkPrivileges($parentUri, '{DAV:}bind'); - } /** - * Triggered before a node is deleted + * 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) { - + public function beforeUnbind($uri) + { list($parentUri) = Uri\split($uri); $this->checkPrivileges($parentUri, '{DAV:}unbind', self::R_RECURSIVEPARENTS); - } /** * Triggered before a node is unlocked. * - * @param string $uri + * @param string $uri * @param DAV\Locks\LockInfo $lock * @TODO: not yet implemented - * @return void */ - function beforeUnlock($uri, DAV\Locks\LockInfo $lock) { - - + public 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 DAV\INode $node * @TODO really should be broken into multiple methods, or even a class. + * * @return bool */ - function propFind(DAV\PropFind $propFind, DAV\INode $node) { - + public function propFind(DAV\PropFind $propFind, DAV\INode $node) + { $path = $propFind->getPath(); // Checking the read permission @@ -1004,95 +961,95 @@ class Plugin extends DAV\ServerPlugin { } return; - } /* Adding principal properties */ if ($node instanceof IPrincipal) { - - $propFind->handle('{DAV:}alternate-URI-set', function() use ($node) { + $propFind->handle('{DAV:}alternate-URI-set', function () use ($node) { return new Href($node->getAlternateUriSet()); }); - $propFind->handle('{DAV:}principal-URL', function() use ($node) { - return new Href($node->getPrincipalUrl() . '/'); + $propFind->handle('{DAV:}principal-URL', function () use ($node) { + return new Href($node->getPrincipalUrl().'/'); }); - $propFind->handle('{DAV:}group-member-set', function() use ($node) { + $propFind->handle('{DAV:}group-member-set', function () use ($node) { $members = $node->getGroupMemberSet(); foreach ($members as $k => $member) { - $members[$k] = rtrim($member, '/') . '/'; + $members[$k] = rtrim($member, '/').'/'; } + return new Href($members); }); - $propFind->handle('{DAV:}group-membership', function() use ($node) { + $propFind->handle('{DAV:}group-membership', function () use ($node) { $members = $node->getGroupMembership(); foreach ($members as $k => $member) { - $members[$k] = rtrim($member, '/') . '/'; + $members[$k] = rtrim($member, '/').'/'; } + return new Href($members); }); $propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']); - } - $propFind->handle('{DAV:}principal-collection-set', function() { - + $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 Href($val); + foreach ($val as $k => $v) { + $val[$k] = $v.'/'; + } + return new Href($val); }); - $propFind->handle('{DAV:}current-user-principal', function() { + $propFind->handle('{DAV:}current-user-principal', function () { if ($url = $this->getCurrentUserPrincipal()) { - return new Xml\Property\Principal(Xml\Property\Principal::HREF, $url . '/'); + return new Xml\Property\Principal(Xml\Property\Principal::HREF, $url.'/'); } else { return new Xml\Property\Principal(Xml\Property\Principal::UNAUTHENTICATED); } }); - $propFind->handle('{DAV:}supported-privilege-set', function() use ($node) { + $propFind->handle('{DAV:}supported-privilege-set', function () use ($node) { return new Xml\Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node)); }); - $propFind->handle('{DAV:}current-user-privilege-set', function() use ($node, $propFind, $path) { + $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); + return new Xml\Property\CurrentUserPrivilegeSet($val); } }); - $propFind->handle('{DAV:}acl', function() use ($node, $propFind, $path) { + $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); + return new Xml\Property\Acl($this->getACL($node)); } }); - $propFind->handle('{DAV:}acl-restrictions', function() { + $propFind->handle('{DAV:}acl-restrictions', function () { return new Xml\Property\AclRestrictions(); }); /* Adding ACL properties */ if ($node instanceof IACL) { - $propFind->handle('{DAV:}owner', function() use ($node) { - return new Href($node->getOwner() . '/'); + $propFind->handle('{DAV:}owner', function () use ($node) { + return new Href($node->getOwner().'/'); }); } - } /** * This method intercepts PROPPATCH methods and make sure the * group-member-set is updated correctly. * - * @param string $path + * @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) { + public function propPatch($path, DAV\PropPatch $propPatch) + { + $propPatch->handle('{DAV:}group-member-set', function ($value) use ($path) { if (is_null($value)) { $memberSet = []; } elseif ($value instanceof Href) { @@ -1116,55 +1073,58 @@ class Plugin extends DAV\ServerPlugin { return true; }); - } /** - * This method handles HTTP REPORT requests + * This method handles HTTP REPORT requests. * * @param string $reportName - * @param mixed $report - * @param mixed $path + * @param mixed $report + * @param mixed $path + * * @return bool */ - function report($reportName, $report, $path) { - + public function report($reportName, $report, $path) + { switch ($reportName) { - - case '{DAV:}principal-property-search' : + case '{DAV:}principal-property-search': $this->server->transactionType = 'report-principal-property-search'; $this->principalPropertySearchReport($path, $report); + return false; - case '{DAV:}principal-search-property-set' : + case '{DAV:}principal-search-property-set': $this->server->transactionType = 'report-principal-search-property-set'; $this->principalSearchPropertySetReport($path, $report); + return false; - case '{DAV:}expand-property' : + case '{DAV:}expand-property': $this->server->transactionType = 'report-expand-property'; $this->expandPropertyReport($path, $report); + return false; - case '{DAV:}principal-match' : + case '{DAV:}principal-match': $this->server->transactionType = 'report-principal-match'; $this->principalMatchReport($path, $report); + return false; - case '{DAV:}acl-principal-prop-set' : + case '{DAV:}acl-principal-prop-set': $this->server->transactionType = 'acl-principal-prop-set'; $this->aclPrincipalPropSetReport($path, $report); + return false; - } - } /** * This method is responsible for handling the 'ACL' event. * - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response + * * @return bool */ - function httpAcl(RequestInterface $request, ResponseInterface $response) { - + public function httpAcl(RequestInterface $request, ResponseInterface $response) + { $path = $request->getPath(); $body = $request->getBodyAsString(); @@ -1192,8 +1152,9 @@ class Plugin extends DAV\ServerPlugin { /* Checking if protected principals from the existing principal set are not overwritten. */ foreach ($oldAcl as $oldAce) { - - if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; + if (!isset($oldAce['protected']) || !$oldAce['protected']) { + continue; + } $found = false; foreach ($newAcl as $newAce) { @@ -1201,36 +1162,35 @@ class Plugin extends DAV\ServerPlugin { $newAce['privilege'] === $oldAce['privilege'] && $newAce['principal'] === $oldAce['principal'] && $newAce['protected'] - ) - $found = true; + ) { + $found = true; + } } - if (!$found) + 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'); + 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'); + 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 (NotFound $e) { - throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist'); + 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'); + throw new Exception\NotRecognizedPrincipal('The specified uri ('.$newAce['principal'].') is not a principal'); } - } $node->setACL($newAcl); @@ -1238,7 +1198,6 @@ class Plugin extends DAV\ServerPlugin { // Breaking the event chain, because we handled this method. return false; - } /* }}} */ @@ -1252,14 +1211,13 @@ class Plugin extends DAV\ServerPlugin { * or a principal URL, the principal URL and principal URLs of groups that * principal belongs to. * - * @param string $path + * @param string $path * @param Xml\Request\PrincipalMatchReport $report - * @return void */ - protected function principalMatchReport($path, Xml\Request\PrincipalMatchReport $report) { - + protected function principalMatchReport($path, Xml\Request\PrincipalMatchReport $report) + { $depth = $this->server->getHTTPDepth(0); - if ($depth !== 0) { + if (0 !== $depth) { throw new BadRequest('The principal-match report is only defined on Depth: 0'); } @@ -1267,20 +1225,15 @@ class Plugin extends DAV\ServerPlugin { $result = []; - if ($report->type === Xml\Request\PrincipalMatchReport::SELF) { - + if (Xml\Request\PrincipalMatchReport::SELF === $report->type) { // Finding all principals under the request uri that match the // current principal. foreach ($currentPrincipals as $currentPrincipal) { - - if ($currentPrincipal === $path || strpos($currentPrincipal, $path . '/') === 0) { + if ($currentPrincipal === $path || 0 === strpos($currentPrincipal, $path.'/')) { $result[] = $currentPrincipal; } - } - } else { - // We need to find all resources that have a property that matches // one of the current principals. $candidates = $this->server->getPropertiesForPath( @@ -1290,7 +1243,6 @@ class Plugin extends DAV\ServerPlugin { ); foreach ($candidates as $candidate) { - if (!isset($candidate[200][$report->principalProperty])) { continue; } @@ -1308,23 +1260,19 @@ class Plugin extends DAV\ServerPlugin { } } } - } $responses = []; foreach ($result as $item) { - $properties = []; if ($report->properties) { - $foo = $this->server->getPropertiesForPath($item, $report->properties); $foo = $foo[0]; $item = $foo['href']; unset($foo['href']); $properties = $foo; - } $responses[] = new DAV\Xml\Element\Response( @@ -1332,7 +1280,6 @@ class Plugin extends DAV\ServerPlugin { $properties, '200' ); - } $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); @@ -1344,8 +1291,6 @@ class Plugin extends DAV\ServerPlugin { $this->server->getBaseUri() ) ); - - } /** @@ -1359,12 +1304,11 @@ class Plugin extends DAV\ServerPlugin { * Other rfc's, such as ACL rely on this report, so it made sense to put * it in this plugin. * - * @param string $path + * @param string $path * @param Xml\Request\ExpandPropertyReport $report - * @return void */ - protected function expandPropertyReport($path, $report) { - + protected function expandPropertyReport($path, $report) + { $depth = $this->server->getHTTPDepth(0); $result = $this->expandProperties($path, $report->properties, $depth); @@ -1377,34 +1321,36 @@ class Plugin extends DAV\ServerPlugin { $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setBody($xml); - } /** * This method expands all the properties and returns - * a list with property values + * a list with property values. * * @param array $path * @param array $requestedProperties the list of required properties - * @param int $depth + * @param int $depth + * * @return array */ - protected function expandProperties($path, array $requestedProperties, $depth) { - + 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; + if (!is_array($childRequestedProperties) || 0 === count($childRequestedProperties)) { + 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 (!array_key_exists($propertyName, $node[200])) { + continue; + } if (!$node[200][$propertyName] instanceof DAV\Xml\Property\Href) { continue; @@ -1416,39 +1362,35 @@ class Plugin extends DAV\ServerPlugin { foreach ($childHrefs as $href) { // Gathering the result of the children $childProps[] = [ - 'name' => '{DAV:}response', - 'value' => $this->expandProperties($href, $childRequestedProperties, 0)[0] + 'name' => '{DAV:}response', + 'value' => $this->expandProperties($href, $childRequestedProperties, 0)[0], ]; } // Replacing the property with its expanded form. $node[200][$propertyName] = $childProps; - } $result[] = new DAV\Xml\Element\Response($node['href'], $node); - } return $result; - } /** - * principalSearchPropertySetReport + * 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 string $path + * @param string $path * @param Xml\Request\PrincipalSearchPropertySetReport $report - * @return void */ - protected function principalSearchPropertySetReport($path, $report) { - + protected function principalSearchPropertySetReport($path, $report) + { $httpDepth = $this->server->getHTTPDepth(0); - if ($httpDepth !== 0) { + if (0 !== $httpDepth) { throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0'); } @@ -1459,7 +1401,6 @@ class Plugin extends DAV\ServerPlugin { $writer->startElement('{DAV:}principal-search-property-set'); foreach ($this->principalSearchPropertySet as $propertyName => $description) { - $writer->startElement('{DAV:}principal-search-property'); $writer->startElement('{DAV:}prop'); @@ -1469,15 +1410,13 @@ class Plugin extends DAV\ServerPlugin { if ($description) { $writer->write([[ - 'name' => '{DAV:}description', - 'value' => $description, - 'attributes' => ['xml:lang' => 'en'] + 'name' => '{DAV:}description', + 'value' => $description, + 'attributes' => ['xml:lang' => 'en'], ]]); } $writer->endElement(); // principal-search-property - - } $writer->endElement(); // principal-search-property-set @@ -1485,27 +1424,25 @@ class Plugin extends DAV\ServerPlugin { $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setStatus(200); $this->server->httpResponse->setBody($writer->outputMemory()); - } /** - * principalPropertySearchReport + * 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 string $path + * @param string $path * @param Xml\Request\PrincipalPropertySearchReport $report - * @return void */ - protected function principalPropertySearchReport($path, Xml\Request\PrincipalPropertySearchReport $report) { - + protected function principalPropertySearchReport($path, Xml\Request\PrincipalPropertySearchReport $report) + { if ($report->applyToPrincipalCollectionSet) { $path = null; } - if ($this->server->getHttpDepth('0') !== 0) { + if (0 !== $this->server->getHttpDepth('0')) { throw new BadRequest('Depth must be 0'); } $result = $this->principalSearch( @@ -1520,12 +1457,11 @@ class Plugin extends DAV\ServerPlugin { $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->server->httpResponse->setBody($this->server->generateMultiStatus($result, 'minimal' === $prefer['return'])); } /** - * aclPrincipalPropSet REPORT + * aclPrincipalPropSet REPORT. * * This method is responsible for handling the {DAV:}acl-principal-prop-set * REPORT, as defined in: @@ -1537,13 +1473,12 @@ class Plugin extends DAV\ServerPlugin { * is used to for example generate a UI with ACL rules, allowing you * to show names for principals for every entry. * - * @param string $path + * @param string $path * @param Xml\Request\AclPrincipalPropSetReport $report - * @return void */ - protected function aclPrincipalPropSetReport($path, Xml\Request\AclPrincipalPropSetReport $report) { - - if ($this->server->getHTTPDepth(0) !== 0) { + protected function aclPrincipalPropSetReport($path, Xml\Request\AclPrincipalPropSetReport $report) + { + if (0 !== $this->server->getHTTPDepth(0)) { throw new BadRequest('The {DAV:}acl-principal-prop-set REPORT only supports Depth 0'); } @@ -1558,14 +1493,12 @@ class Plugin extends DAV\ServerPlugin { $principals = []; foreach ($acl['{DAV:}acl']->getPrivileges() as $ace) { - - if ($ace['principal'][0] === '{') { + if ('{' === $ace['principal'][0]) { // It's not a principal, it's one of the special rules such as {DAV:}authenticated continue; } $principals[] = $ace['principal']; - } $properties = $this->server->getPropertiesForMultiplePaths( @@ -1578,10 +1511,8 @@ class Plugin extends DAV\ServerPlugin { $this->server->httpResponse->setBody( $this->server->generateMultiStatus($properties) ); - } - /* }}} */ /** @@ -1590,13 +1521,15 @@ class Plugin extends DAV\ServerPlugin { * can use to create new calendars. * * @param DAV\INode $node - * @param string $output + * @param string $output + * * @return bool */ - function htmlActionsPanel(DAV\INode $node, &$output) { - - if (!$node instanceof PrincipalCollection) + public function htmlActionsPanel(DAV\INode $node, &$output) + { + if (!$node instanceof PrincipalCollection) { return; + } $output .= '

Create new principal

@@ -1610,7 +1543,6 @@ class Plugin extends DAV\ServerPlugin { '; return false; - } /** @@ -1624,13 +1556,12 @@ class Plugin extends DAV\ServerPlugin { * * @return array */ - function getPluginInfo() { - + public function getPluginInfo() + { return [ - 'name' => $this->getPluginName(), + 'name' => $this->getPluginName(), 'description' => 'Adds support for WebDAV ACL (rfc3744)', - 'link' => 'http://sabre.io/dav/acl/', + 'link' => 'http://sabre.io/dav/acl/', ]; - } } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Principal.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Principal.php index d7db9499946..e2df1c35e57 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Principal.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Principal.php @@ -1,12 +1,14 @@ principalBackend = $principalBackend; $this->principalProperties = $principalProperties; - } /** - * Returns the full principal url + * Returns the full principal url. * * @return string */ - function getPrincipalUrl() { - + public function getPrincipalUrl() + { return $this->principalProperties['uri']; - } /** - * Returns a list of alternative urls for a principal + * Returns a list of alternative urls for a principal. * * This can for example be an email address, or ldap url. * * @return array */ - function getAlternateUriSet() { - + public 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']; + $uris[] = 'mailto:'.$this->principalProperties['{http://sabredav.org/ns}email-address']; } return array_unique($uris); - } /** - * Returns the list of group members + * 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() { - + public function getGroupMemberSet() + { return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']); - } /** - * Returns the list of groups this principal is member of + * 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() { - + public function getGroupMembership() + { return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']); - } /** - * Sets a list of group members + * 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. @@ -126,12 +121,10 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL { * This method should throw an exception if the members could not be set. * * @param array $groupMembers - * @return void */ - function setGroupMemberSet(array $groupMembers) { - + public function setGroupMemberSet(array $groupMembers) + { $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers); - } /** @@ -139,48 +132,45 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL { * * @return string */ - function getName() { - + public function getName() + { $uri = $this->principalProperties['uri']; - list(, $name) = URLUtil::splitPath($uri); - return $name; + list(, $name) = Uri\split($uri); + return $name; } /** - * Returns the name of the user + * Returns the name of the user. * * @return string */ - function getDisplayName() { - + public function getDisplayName() + { if (isset($this->principalProperties['{DAV:}displayname'])) { return $this->principalProperties['{DAV:}displayname']; } else { return $this->getName(); } - } /** - * Returns a list of properties + * Returns a list of properties. * * @param array $requestedProperties + * * @return array */ - function getProperties($requestedProperties) { - + public function getProperties($requestedProperties) + { $newProperties = []; foreach ($requestedProperties as $propName) { - if (isset($this->principalProperties[$propName])) { $newProperties[$propName] = $this->principalProperties[$propName]; } - } return $newProperties; - } /** @@ -193,29 +183,24 @@ class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL { * Read the PropPatch documentation for more information. * * @param DAV\PropPatch $propPatch - * @return void */ - function propPatch(DAV\PropPatch $propPatch) { - + public function propPatch(DAV\PropPatch $propPatch) + { return $this->principalBackend->updatePrincipal( $this->principalProperties['uri'], $propPatch ); - } /** - * Returns the owner principal + * Returns the owner principal. * * This must be a url to a principal, or null if there's no owner * * @return string|null */ - function getOwner() { - + public function getOwner() + { return $this->principalProperties['uri']; - - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php index 9bf9ba4453c..03a9c4badd2 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php @@ -1,9 +1,11 @@ searchPrincipals( @@ -47,7 +50,5 @@ abstract class AbstractBackend implements BackendInterface { if ($result) { return $result[0]; } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php index 40b6e33ead9..40ac272b5a6 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php @@ -1,5 +1,7 @@ [ 'dbField' => 'displayname', ], - /** + /* * This is the users' primary email-address. */ '{http://sabredav.org/ns}email-address' => [ @@ -67,10 +68,9 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { * * @param \PDO $pdo */ - function __construct(\PDO $pdo) { - + public function __construct(\PDO $pdo) + { $this->pdo = $pdo; - } /** @@ -87,10 +87,11 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { * you have an email address, use this property. * * @param string $prefixPath + * * @return array */ - function getPrincipalsByPrefix($prefixPath) { - + public function getPrincipalsByPrefix($prefixPath) + { $fields = [ 'uri', ]; @@ -98,15 +99,16 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { foreach ($this->fieldMap as $key => $value) { $fields[] = $value['dbField']; } - $result = $this->pdo->query('SELECT ' . implode(',', $fields) . ' FROM ' . $this->tableName); + $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; + list($rowPrefix) = Uri\split($row['uri']); + if ($rowPrefix !== $prefixPath) { + continue; + } $principal = [ 'uri' => $row['uri'], @@ -117,11 +119,9 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { } } $principals[] = $principal; - } return $principals; - } /** @@ -130,10 +130,11 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { * getPrincipalsByPrefix. * * @param string $path + * * @return array */ - function getPrincipalByPath($path) { - + public function getPrincipalByPath($path) + { $fields = [ 'id', 'uri', @@ -142,14 +143,16 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { foreach ($this->fieldMap as $key => $value) { $fields[] = $value['dbField']; } - $stmt = $this->pdo->prepare('SELECT ' . implode(',', $fields) . ' FROM ' . $this->tableName . ' WHERE uri = ?'); + $stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '.$this->tableName.' WHERE uri = ?'); $stmt->execute([$path]); $row = $stmt->fetch(\PDO::FETCH_ASSOC); - if (!$row) return; + if (!$row) { + return; + } $principal = [ - 'id' => $row['id'], + 'id' => $row['id'], 'uri' => $row['uri'], ]; foreach ($this->fieldMap as $key => $value) { @@ -157,8 +160,8 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { $principal[$key] = $row[$value['dbField']]; } } - return $principal; + return $principal; } /** @@ -173,41 +176,36 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { * * Read the PropPatch documentation for more info and examples. * - * @param string $path + * @param string $path * @param DAV\PropPatch $propPatch */ - function updatePrincipal($path, DAV\PropPatch $propPatch) { - - $propPatch->handle(array_keys($this->fieldMap), function($properties) use ($path) { - - $query = "UPDATE " . $this->tableName . " SET "; + public function updatePrincipal($path, 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; + $query .= $dbField.' = :'.$dbField; $values[$dbField] = $value; - } - $query .= " WHERE uri = :uri"; + $query .= ' WHERE uri = :uri'; $values['uri'] = $path; $stmt = $this->pdo->prepare($query); $stmt->execute($values); return true; - }); - } /** @@ -235,48 +233,52 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { * from working. * * @param string $prefixPath - * @param array $searchProperties + * @param array $searchProperties * @param string $test + * * @return array */ - function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { - if (count($searchProperties) == 0) return []; //No criteria + public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') + { + if (0 == count($searchProperties)) { + return []; + } //No criteria - $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE '; + $query = 'SELECT uri FROM '.$this->tableName.' WHERE '; $values = []; foreach ($searchProperties as $property => $value) { switch ($property) { - case '{DAV:}displayname' : - $column = "displayname"; + case '{DAV:}displayname': + $column = 'displayname'; break; - case '{http://sabredav.org/ns}email-address' : - $column = "email"; + case '{http://sabredav.org/ns}email-address': + $column = 'email'; break; - default : + default: // Unsupported property return []; } - if (count($values) > 0) $query .= (strcmp($test, "anyof") == 0 ? " OR " : " AND "); - $query .= 'lower(' . $column . ') LIKE lower(?)'; - $values[] = '%' . $value . '%'; - + if (count($values) > 0) { + $query .= (0 == strcmp($test, 'anyof') ? ' OR ' : ' AND '); + } + $query .= 'lower('.$column.') LIKE lower(?)'; + $values[] = '%'.$value.'%'; } $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; + list($rowPrefix) = Uri\split($row['uri']); + if ($rowPrefix !== $prefixPath) { + continue; + } $principals[] = $row['uri']; - } return $principals; - } /** @@ -294,26 +296,32 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { * * @param string $uri * @param string $principalPrefix + * * @return string */ - function findByUri($uri, $principalPrefix) { + public function findByUri($uri, $principalPrefix) + { $value = null; $scheme = null; - list($scheme, $value) = explode(":", $uri, 2); - if (empty($value)) return null; + list($scheme, $value) = explode(':', $uri, 2); + if (empty($value)) { + return null; + } $uri = null; - switch ($scheme){ - case "mailto": - $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE lower(email)=lower(?)'; + switch ($scheme) { + case 'mailto': + $query = 'SELECT uri FROM '.$this->tableName.' WHERE lower(email)=lower(?)'; $stmt = $this->pdo->prepare($query); $stmt->execute([$value]); - + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { // Checking if the principal is in the prefix - list($rowPrefix) = URLUtil::splitPath($row['uri']); - if ($rowPrefix !== $principalPrefix) continue; - + list($rowPrefix) = Uri\split($row['uri']); + if ($rowPrefix !== $principalPrefix) { + continue; + } + $uri = $row['uri']; break; //Stop on first match } @@ -322,51 +330,56 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { //unsupported uri scheme return null; } + return $uri; } /** - * Returns the list of members for a group-principal + * Returns the list of members for a group-principal. * * @param string $principal + * * @return array */ - function getGroupMemberSet($principal) { - + public 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 = ?'); + 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; + return $result; } /** - * Returns the list of groups a principal is a member of + * Returns the list of groups a principal is a member of. * * @param string $principal + * * @return array */ - function getGroupMembership($principal) { - + public 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 = ?'); + 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; + return $result; } /** @@ -375,13 +388,12 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { * The principals should be passed as a list of uri's. * * @param string $principal - * @param array $members - * @return void + * @param array $members */ - function setGroupMemberSet($principal, array $members) { - + public 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 = $this->pdo->prepare('SELECT id, uri FROM '.$this->tableName.' WHERE uri IN (? '.str_repeat(', ? ', count($members)).');'); $stmt->execute(array_merge([$principal], $members)); $memberIds = []; @@ -394,19 +406,17 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { $memberIds[] = $row['id']; } } - if (!$principalId) throw new DAV\Exception('Principal not found'); - + 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 = $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 = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);'); $stmt->execute([$principalId, $memberId]); - } - } /** @@ -417,15 +427,12 @@ class PDO extends AbstractBackend implements CreatePrincipalSupport { * of the principal. * * @param string $path - * @param MkCol $mkCol - * @return void + * @param MkCol $mkCol */ - function createPrincipal($path, MkCol $mkCol) { - - $stmt = $this->pdo->prepare('INSERT INTO ' . $this->tableName . ' (uri) VALUES (?)'); + public function createPrincipal($path, MkCol $mkCol) + { + $stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (uri) VALUES (?)'); $stmt->execute([$path]); $this->updatePrincipal($path, $mkCol); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalCollection.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalCollection.php index ee5b88a904e..4fae96e0cfe 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalCollection.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/PrincipalCollection.php @@ -1,5 +1,7 @@ principalBackend, $principal); - } /** @@ -56,21 +58,20 @@ class PrincipalCollection extends AbstractPrincipalCollection implements IExtend * property for you. * * @param string $name - * @param MkCol $mkCol + * @param MkCol $mkCol + * * @throws InvalidResourceType - * @return void */ - function createExtendedCollection($name, MkCol $mkCol) { - + public function createExtendedCollection($name, MkCol $mkCol) + { if (!$mkCol->hasResourceType('{DAV:}principal')) { throw new InvalidResourceType('Only resources of type {DAV:}principal may be created here'); } $this->principalBackend->createPrincipal( - $this->principalPrefix . '/' . $name, + $this->principalPrefix.'/'.$name, $mkCol ); - } /** @@ -85,7 +86,8 @@ class PrincipalCollection extends AbstractPrincipalCollection implements IExtend * * @return array */ - function getACL() { + public function getACL() + { return [ [ 'principal' => '{DAV:}authenticated', @@ -94,5 +96,4 @@ class PrincipalCollection extends AbstractPrincipalCollection implements IExtend ], ]; } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Acl.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Acl.php index 0e1c30ccfea..372f62ab3d7 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Acl.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Acl.php @@ -1,5 +1,7 @@ privileges = $privileges; $this->prefixBaseUrl = $prefixBaseUrl; - } /** - * Returns the list of privileges for this property + * Returns the list of privileges for this property. * * @return array */ - function getPrivileges() { - + public function getPrivileges() + { return $this->privileges; - } /** @@ -94,16 +94,12 @@ class Acl implements Element, HtmlOutput { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->privileges as $ace) { - $this->serializeAce($writer, $ace); - } - } /** @@ -118,32 +114,33 @@ class Acl implements Element, HtmlOutput { * be used to construct local links. * * @param HtmlOutputHelper $html + * * @return string */ - function toHtml(HtmlOutputHelper $html) { - + public function toHtml(HtmlOutputHelper $html) + { ob_start(); - echo ""; - echo ""; + echo '
PrincipalPrivilege
'; + echo ''; foreach ($this->privileges as $privilege) { - echo ''; // if it starts with a {, it's a special principal - if ($privilege['principal'][0] === '{') { + if ('{' === $privilege['principal'][0]) { echo ''; } else { echo ''; } echo ''; echo ''; echo ''; - } - echo "
PrincipalPrivilege
', $html->xmlName($privilege['principal']), '', $html->link($privilege['principal']), '', $html->xmlName($privilege['privilege']), ''; - if (!empty($privilege['protected'])) echo '(protected)'; + if (!empty($privilege['protected'])) { + echo '(protected)'; + } echo '
"; - return ob_get_clean(); + echo ''; + return ob_get_clean(); } /** @@ -165,21 +162,21 @@ class Acl implements Element, HtmlOutput { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $elementMap = [ - '{DAV:}ace' => 'Sabre\Xml\Element\KeyValue', + '{DAV:}ace' => 'Sabre\Xml\Element\KeyValue', '{DAV:}privilege' => 'Sabre\Xml\Element\Elements', '{DAV:}principal' => 'Sabre\DAVACL\Xml\Property\Principal', ]; $privileges = []; - foreach ((array)$reader->parseInnerTree($elementMap) as $element) { - - if ($element['name'] !== '{DAV:}ace') { + foreach ((array) $reader->parseInnerTree($elementMap) as $element) { + if ('{DAV:}ace' !== $element['name']) { continue; } $ace = $element['value']; @@ -190,19 +187,18 @@ class Acl implements Element, HtmlOutput { $principal = $ace['{DAV:}principal']; switch ($principal->getType()) { - case Principal::HREF : + case Principal::HREF: $principal = $principal->getHref(); break; - case Principal::AUTHENTICATED : + case Principal::AUTHENTICATED: $principal = '{DAV:}authenticated'; break; - case Principal::UNAUTHENTICATED : + case Principal::UNAUTHENTICATED: $principal = '{DAV:}unauthenticated'; break; - case Principal::ALL : + case Principal::ALL: $principal = '{DAV:}all'; break; - } $protected = array_key_exists('{DAV:}protected', $ace); @@ -211,7 +207,7 @@ class Acl implements Element, HtmlOutput { throw new DAV\Exception\NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported'); } foreach ($ace['{DAV:}grant'] as $elem) { - if ($elem['name'] !== '{DAV:}privilege') { + if ('{DAV:}privilege' !== $elem['name']) { continue; } @@ -222,34 +218,30 @@ class Acl implements Element, HtmlOutput { 'privilege' => $priv, ]; } - } - } return new self($privileges); - } /** * Serializes a single access control entry. * * @param Writer $writer - * @param array $ace - * @return void + * @param array $ace */ - private function serializeAce(Writer $writer, array $ace) { - + private function serializeAce(Writer $writer, array $ace) + { $writer->startElement('{DAV:}ace'); switch ($ace['principal']) { - case '{DAV:}authenticated' : + case '{DAV:}authenticated': $principal = new Principal(Principal::AUTHENTICATED); break; - case '{DAV:}unauthenticated' : + case '{DAV:}unauthenticated': $principal = new Principal(Principal::UNAUTHENTICATED); break; - case '{DAV:}all' : + case '{DAV:}all': $principal = new Principal(Principal::ALL); break; default: @@ -271,7 +263,5 @@ class Acl implements Element, HtmlOutput { } $writer->endElement(); // ace - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php index 8d5854c23c0..0bf16b43173 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php @@ -1,12 +1,14 @@ writeElement('{DAV:}grant-only'); $writer->writeElement('{DAV:}no-invert'); - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php index 74c09cee150..08385633025 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php @@ -1,5 +1,7 @@ privileges = $privileges; - } /** @@ -57,19 +58,14 @@ class CurrentUserPrivilegeSet implements Element, HtmlOutput { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { foreach ($this->privileges as $privName) { - $writer->startElement('{DAV:}privilege'); $writer->writeElement($privName); $writer->endElement(); - } - - } /** @@ -77,12 +73,12 @@ class CurrentUserPrivilegeSet implements Element, HtmlOutput { * list. * * @param string $privilegeName + * * @return bool */ - function has($privilegeName) { - + public function has($privilegeName) + { return in_array($privilegeName, $this->privileges); - } /** @@ -90,10 +86,9 @@ class CurrentUserPrivilegeSet implements Element, HtmlOutput { * * @return array */ - function getValue() { - + public function getValue() + { return $this->privileges; - } /** @@ -115,21 +110,22 @@ class CurrentUserPrivilegeSet implements Element, HtmlOutput { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $result = []; $tree = $reader->parseInnerTree(['{DAV:}privilege' => 'Sabre\\Xml\\Element\\Elements']); foreach ($tree as $element) { - if ($element['name'] !== '{DAV:}privilege') { + if ('{DAV:}privilege' !== $element['name']) { continue; } $result[] = $element['value'][0]; } - return new self($result); + return new self($result); } /** @@ -144,16 +140,14 @@ class CurrentUserPrivilegeSet implements Element, HtmlOutput { * be used to construct local links. * * @param HtmlOutputHelper $html + * * @return string */ - function toHtml(HtmlOutputHelper $html) { - + public function toHtml(HtmlOutputHelper $html) + { return implode( ', ', array_map([$html, 'xmlName'], $this->getValue()) ); - } - - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Principal.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Principal.php index 04d22165d27..e304a2aed1e 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Principal.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/Principal.php @@ -1,5 +1,7 @@ type = $type; - if ($type === self::HREF && is_null($href)) { + if (self::HREF === $type && is_null($href)) { throw new DAV\Exception('The href argument must be specified for the HREF principal type.'); } if ($href) { - $href = rtrim($href, '/') . '/'; + $href = rtrim($href, '/').'/'; parent::__construct($href); } - } /** - * Returns the principal type + * Returns the principal type. * * @return int */ - function getType() { - + public function getType() + { return $this->type; - } - /** * The xmlSerialize method is called during xml writing. * @@ -101,26 +100,23 @@ class Principal extends DAV\Xml\Property\Href { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { switch ($this->type) { - - case self::UNAUTHENTICATED : + case self::UNAUTHENTICATED: $writer->writeElement('{DAV:}unauthenticated'); break; - case self::AUTHENTICATED : + case self::AUTHENTICATED: $writer->writeElement('{DAV:}authenticated'); break; - case self::HREF : + case self::HREF: parent::xmlSerialize($writer); break; - case self::ALL : + case self::ALL: $writer->writeElement('{DAV:}all'); break; } - } /** @@ -135,22 +131,21 @@ class Principal extends DAV\Xml\Property\Href { * be used to construct local links. * * @param HtmlOutputHelper $html + * * @return string */ - function toHtml(HtmlOutputHelper $html) { - + public function toHtml(HtmlOutputHelper $html) + { switch ($this->type) { - - case self::UNAUTHENTICATED : + case self::UNAUTHENTICATED: return 'unauthenticated'; - case self::AUTHENTICATED : + case self::AUTHENTICATED: return 'authenticated'; - case self::HREF : + case self::HREF: return parent::toHtml($html); - case self::ALL : + case self::ALL: return 'all'; } - } /** @@ -172,25 +167,24 @@ class Principal extends DAV\Xml\Property\Href { * the next element. * * @param Reader $reader + * * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $tree = $reader->parseInnerTree()[0]; switch ($tree['name']) { - case '{DAV:}unauthenticated' : + case '{DAV:}unauthenticated': return new self(self::UNAUTHENTICATED); - case '{DAV:}authenticated' : + case '{DAV:}authenticated': return new self(self::AUTHENTICATED); case '{DAV:}href': return new self(self::HREF, $tree['value']); case '{DAV:}all': return new self(self::ALL); - default : - throw new BadRequest('Unknown or unsupported principal type: ' . $tree['name']); + default: + throw new BadRequest('Unknown or unsupported principal type: '.$tree['name']); } - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php index b963cc8c3ba..1762391d989 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php @@ -1,5 +1,7 @@ privileges = $privileges; - } /** @@ -46,10 +47,9 @@ class SupportedPrivilegeSet implements XmlSerializable, HtmlOutput { * * @return array */ - function getValue() { - + public function getValue() + { return $this->privileges; - } /** @@ -69,12 +69,10 @@ class SupportedPrivilegeSet implements XmlSerializable, HtmlOutput { * If you are opening new elements, you must also close them again. * * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { $this->serializePriv($writer, '{DAV:}all', ['aggregates' => $this->privileges]); - } /** @@ -89,52 +87,49 @@ class SupportedPrivilegeSet implements XmlSerializable, HtmlOutput { * be used to construct local links. * * @param HtmlOutputHelper $html + * * @return string */ - function toHtml(HtmlOutputHelper $html) { - - $traverse = function($privName, $priv) use (&$traverse, $html) { - echo "
  • "; + public function toHtml(HtmlOutputHelper $html) + { + $traverse = function ($privName, $priv) use (&$traverse, $html) { + echo '
  • '; echo $html->xmlName($privName); if (isset($priv['abstract']) && $priv['abstract']) { - echo " (abstract)"; + echo ' (abstract)'; } if (isset($priv['description'])) { - echo " " . $html->h($priv['description']); + echo ' '.$html->h($priv['description']); } if (isset($priv['aggregates'])) { echo "\n
      \n"; foreach ($priv['aggregates'] as $subPrivName => $subPriv) { $traverse($subPrivName, $subPriv); } - echo "
    "; + echo ''; } echo "
  • \n"; }; ob_start(); - echo "
      "; + echo '
        '; $traverse('{DAV:}all', ['aggregates' => $this->getValue()]); echo "
      \n"; return ob_get_clean(); - } - - /** - * Serializes a property + * Serializes a property. * * This is a recursive function. * * @param Writer $writer * @param string $privName - * @param array $privilege - * @return void + * @param array $privilege */ - private function serializePriv(Writer $writer, $privName, $privilege) { - + private function serializePriv(Writer $writer, $privName, $privilege) + { $writer->startElement('{DAV:}supported-privilege'); $writer->startElement('{DAV:}privilege'); @@ -154,7 +149,5 @@ class SupportedPrivilegeSet implements XmlSerializable, HtmlOutput { } $writer->endElement(); // supported-privilege - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php index 0aa2f29a559..17b37afabbf 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/AclPrincipalPropSetReport.php @@ -1,5 +1,7 @@ pushContext(); $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Deserializer\enum'; @@ -61,7 +64,5 @@ class AclPrincipalPropSetReport implements XmlDeserializable { } return $report; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php index a9938ba5bf0..393308cd2d7 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php @@ -1,5 +1,7 @@ parseInnerTree(); $obj = new self(); $obj->properties = self::traverse($elems); return $obj; - } /** @@ -69,15 +71,15 @@ class ExpandPropertyReport implements XmlDeserializable { * {DAV:}property elements. * * @param array $elems - * @return void + * + * @return array */ - private static function traverse($elems) { - + private static function traverse($elems) + { $result = []; foreach ($elems as $elem) { - - if ($elem['name'] !== '{DAV:}property') { + if ('{DAV:}property' !== $elem['name']) { continue; } @@ -85,7 +87,7 @@ class ExpandPropertyReport implements XmlDeserializable { $elem['attributes']['namespace'] : 'DAV:'; - $propName = '{' . $namespace . '}' . $elem['attributes']['name']; + $propName = '{'.$namespace.'}'.$elem['attributes']['name']; $value = null; if (is_array($elem['value'])) { @@ -93,11 +95,8 @@ class ExpandPropertyReport implements XmlDeserializable { } $result[$propName] = $value; - } return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php index 1be15ab2da6..f868cc9df64 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalMatchReport.php @@ -1,5 +1,7 @@ pushContext(); $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Deserializer\enum'; @@ -101,7 +104,5 @@ class PrincipalMatchReport implements XmlDeserializable { } return $principalMatch; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php index b0cf0e408ff..26468fd2163 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php @@ -1,5 +1,7 @@ test = 'allof'; - if ($reader->getAttribute('test') === 'anyof') { + if ('anyof' === $reader->getAttribute('test')) { $self->test = 'anyof'; } $elemMap = [ '{DAV:}property-search' => 'Sabre\\Xml\\Element\\KeyValue', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', ]; - + foreach ($reader->parseInnerTree($elemMap) as $elem) { - switch ($elem['name']) { - - case '{DAV:}prop' : + case '{DAV:}prop': $self->properties = array_keys($elem['value']); break; - case '{DAV:}property-search' : + case '{DAV:}property-search': $foundSearchProp = true; // This property has two sub-elements: // {DAV:}prop - The property to be searched on. This may @@ -109,19 +110,15 @@ class PrincipalPropertySearchReport implements XmlDeserializable { $self->searchProperties[$propName] = $elem['value']['{DAV:}match']; } break; - case '{DAV:}apply-to-principal-collection-set' : + case '{DAV:}apply-to-principal-collection-set': $self->applyToPrincipalCollectionSet = true; break; - } - } if (!$foundSearchProp) { throw new BadRequest('The {DAV:}principal-property-search report must contain at least 1 {DAV:}property-search element'); } return $self; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php index 64d1f7f861e..37bc2cfbfac 100644 --- a/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php +++ b/htdocs/includes/sabre/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php @@ -1,5 +1,7 @@ isEmptyElement) { throw new BadRequest('The {DAV:}principal-search-property-set element must be empty'); } @@ -51,8 +54,7 @@ class PrincipalSearchPropertySetReport implements XmlDeserializable { $reader->next(); $self = new self(); + return $self; - } - } diff --git a/htdocs/includes/sabre/sabre/dav/phpstan.neon b/htdocs/includes/sabre/sabre/dav/phpstan.neon new file mode 100644 index 00000000000..e50c5be6e0d --- /dev/null +++ b/htdocs/includes/sabre/sabre/dav/phpstan.neon @@ -0,0 +1,3 @@ +parameters: + level: 0 + bootstrap: %currentWorkingDirectory%/vendor/autoload.php diff --git a/htdocs/includes/sabre/sabre/event/.travis.yml b/htdocs/includes/sabre/sabre/event/.travis.yml index b6719f591f7..7089e370b7d 100644 --- a/htdocs/includes/sabre/sabre/event/.travis.yml +++ b/htdocs/includes/sabre/sabre/event/.travis.yml @@ -1,9 +1,6 @@ language: php php: - - 5.5 - - 5.6 - 7 - - hhvm matrix: allow_failures: diff --git a/htdocs/includes/sabre/sabre/event/CHANGELOG.md b/htdocs/includes/sabre/sabre/event/CHANGELOG.md index 9d6d7cfaaf8..f46e9d0f76b 100644 --- a/htdocs/includes/sabre/sabre/event/CHANGELOG.md +++ b/htdocs/includes/sabre/sabre/event/CHANGELOG.md @@ -1,6 +1,32 @@ ChangeLog ========= +5.0.0 (2016-10-23) +------------------ + +* #42: The `coroutine` function now supports `return` in the passed generator + function. This allows you to more generally return a value. This is a BC + break as this is a feature that was only made possible with PHP 7, and + before the coroutine function would only ever return the last thing that + was yielded. If you depended on that feature, replace your last `yield` with + a `return`. + + +4.0.0 (2016-09-19) +------------------ + +* sabre/event now requires PHP 7. If you need PHP 5.5 support, just keep + using 3.0.0. +* PHP 7 type hints are now used everywhere. We're also using strict_types. +* Support for a new `WildcardEmitter` which allows you to listen for events + using the `*` wildcard. +* Removed deprecated functions `Promise::error` and `Promise::all`. Instead, + use `Promise::otherwise` and `Promise\all()`. +* `EventEmitter`, `EventEmitterTrait` and `EventEmitterInterface` are now just + called `Emitter`, `EmitterTrait`, and `EmitterInterface`. +* When rejecting Promises, it's now _required_ to use an `Exception` or + `Throwable`. This makes the typical case simpler and reduces special cases. + 3.0.0 (2015-11-05) ------------------ diff --git a/htdocs/includes/sabre/sabre/event/LICENSE b/htdocs/includes/sabre/sabre/event/LICENSE index 9a495cef0a1..962a49267ab 100644 --- a/htdocs/includes/sabre/sabre/event/LICENSE +++ b/htdocs/includes/sabre/sabre/event/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2013-2015 fruux GmbH (https://fruux.com/) +Copyright (C) 2013-2016 fruux GmbH (https://fruux.com/) All rights reserved. diff --git a/htdocs/includes/sabre/sabre/event/README.md b/htdocs/includes/sabre/sabre/event/README.md index 364906fd496..a20473fd5d5 100644 --- a/htdocs/includes/sabre/sabre/event/README.md +++ b/htdocs/includes/sabre/sabre/event/README.md @@ -17,7 +17,7 @@ Installation Make sure you have [composer][3] installed, and then run: - composer require sabre/event "~3.0.0" + composer require sabre/event "^3.0" This package requires PHP 5.5. The 2.0 branch is still maintained as well, and supports PHP 5.4. @@ -28,6 +28,7 @@ Build status | branch | status | | ------ | ------ | | master | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=master)](https://travis-ci.org/fruux/sabre-event) | +| 3.0 | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=2.0)](https://travis-ci.org/fruux/sabre-event) | | 2.0 | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=2.0)](https://travis-ci.org/fruux/sabre-event) | | 1.0 | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=1.0)](https://travis-ci.org/fruux/sabre-event) | | php53 | [![Build Status](https://travis-ci.org/fruux/sabre-event.svg?branch=php53)](https://travis-ci.org/fruux/sabre-event) | diff --git a/htdocs/includes/sabre/sabre/event/composer.json b/htdocs/includes/sabre/sabre/event/composer.json index 9a11b01aaa4..6eb192e7a19 100644 --- a/htdocs/includes/sabre/sabre/event/composer.json +++ b/htdocs/includes/sabre/sabre/event/composer.json @@ -8,12 +8,15 @@ "Hooks", "Plugin", "Signal", - "Async" + "Async", + "EventLoop", + "Reactor", + "Coroutine" ], "homepage": "http://sabre.io/event/", "license": "BSD-3-Clause", "require": { - "php": ">=5.5" + "php": ">=7.0" }, "authors": [ { @@ -38,7 +41,7 @@ ] }, "require-dev": { - "sabre/cs": "~0.0.4", + "sabre/cs": "~1.0.0", "phpunit/phpunit" : "*" }, "config" : { diff --git a/htdocs/includes/sabre/sabre/event/examples/curl.php b/htdocs/includes/sabre/sabre/event/examples/curl.php new file mode 100644 index 00000000000..c2455760bc0 --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/examples/curl.php @@ -0,0 +1,131 @@ +#!/usr/bin/env php +curlMultiHandle = curl_multi_init(); + + } + + function addHandle($curlHandle) { + + curl_multi_add_handle($mh, $curlHandle); + + } + +} + +$active = null; + +function curl_multi_loop_scheduler($mh, callable $done) { + + $mrc = curl_multi_exec($mh, $active); + switch ($mrc) { + + /** + * From the curl docs. If CURM_CALL_MULTI_PERFORM is returned, simply + * call curl_multi_perform immediately again. In PHP this means we + * actually call curl_multi_exec immediately again. + * + * We're doing this in the next tick. + */ + case CURLM_CALL_MULTI_PERFORM : + Loop\nextTick(function() use ($mh, $done) { + + curl_multi_loop_scheduler($mh, $done); + + }); + break; + case CURLM_OK : + if (!$active) { + // We're done! + $done(); + return; + } + // Check again after 0.02 seconds + Loop\setTimeout(function() use ($mh, $done) { + + curl_multi_loop_scheduler($mh, $done); + + }, 0.02); + break; + + default : + throw Exception('Curl error: ' . curl_multi_strerror($mrc)); + + } + +} + +curl_multi_loop_scheduler($mh, function() { + + echo "Success!\n"; + +}); + +Loop\run(); + +//close the handles +curl_multi_remove_handle($mh, $ch1); +curl_multi_remove_handle($mh, $ch2); +curl_multi_close($mh); diff --git a/htdocs/includes/sabre/sabre/event/examples/promise.php b/htdocs/includes/sabre/sabre/event/examples/promise.php old mode 100644 new mode 100755 index b40227e15dd..16ef3c53d71 --- a/htdocs/includes/sabre/sabre/event/examples/promise.php +++ b/htdocs/includes/sabre/sabre/event/examples/promise.php @@ -1,8 +1,8 @@ #!/usr/bin/env php -wait(); diff --git a/htdocs/includes/sabre/sabre/event/examples/tail.php b/htdocs/includes/sabre/sabre/event/examples/tail.php old mode 100644 new mode 100755 index d4e82206b52..334bd23f8bd --- a/htdocs/includes/sabre/sabre/event/examples/tail.php +++ b/htdocs/includes/sabre/sabre/event/examples/tail.php @@ -1,5 +1,5 @@ #!/usr/bin/env php -run(); +\Sabre\Event\Loop\run(); diff --git a/htdocs/includes/sabre/sabre/event/lib/Emitter.php b/htdocs/includes/sabre/sabre/event/lib/Emitter.php new file mode 100644 index 00000000000..1c5b2462c4e --- /dev/null +++ b/htdocs/includes/sabre/sabre/event/lib/Emitter.php @@ -0,0 +1,18 @@ +listeners[$eventName])) { $this->listeners[$eventName] = [ @@ -51,12 +42,9 @@ trait EventEmitterTrait { /** * Subscribe to an event exactly once. * - * @param string $eventName - * @param callable $callBack - * @param int $priority * @return void */ - function once($eventName, callable $callBack, $priority = 100) { + function once(string $eventName, callable $callBack, int $priority = 100) { $wrapper = null; $wrapper = function() use ($eventName, $callBack, &$wrapper) { @@ -90,13 +78,8 @@ trait EventEmitterTrait { * * Lastly, if there are 5 event handlers for an event. The continueCallback * will be called at most 4 times. - * - * @param string $eventName - * @param array $arguments - * @param callback $continueCallBack - * @return bool */ - function emit($eventName, array $arguments = [], callable $continueCallBack = null) { + function emit(string $eventName, array $arguments = [], callable $continueCallBack = null) : bool { if (is_null($continueCallBack)) { @@ -139,10 +122,9 @@ trait EventEmitterTrait { * The list is returned as an array, and the list of events are sorted by * their priority. * - * @param string $eventName * @return callable[] */ - function listeners($eventName) { + function listeners(string $eventName) : array { if (!isset($this->listeners[$eventName])) { return []; @@ -167,12 +149,8 @@ trait EventEmitterTrait { * * If the listener could not be found, this method will return false. If it * was removed it will return true. - * - * @param string $eventName - * @param callable $listener - * @return bool */ - function removeListener($eventName, callable $listener) { + function removeListener(string $eventName, callable $listener) : bool { if (!isset($this->listeners[$eventName])) { return false; @@ -195,10 +173,9 @@ trait EventEmitterTrait { * removed. If it is not specified, every listener for every event is * removed. * - * @param string $eventName * @return void */ - function removeAllListeners($eventName = null) { + function removeAllListeners(string $eventName = null) { if (!is_null($eventName)) { unset($this->listeners[$eventName]); @@ -208,4 +185,11 @@ trait EventEmitterTrait { } + /** + * The list of listeners + * + * @var array + */ + protected $listeners = []; + } diff --git a/htdocs/includes/sabre/sabre/event/lib/EventEmitter.php b/htdocs/includes/sabre/sabre/event/lib/EventEmitter.php index 1bb1c3cf92b..cae6ac2a6ad 100644 --- a/htdocs/includes/sabre/sabre/event/lib/EventEmitter.php +++ b/htdocs/includes/sabre/sabre/event/lib/EventEmitter.php @@ -1,18 +1,19 @@ -runNextTicks(); $nextTimeout = $this->runTimers(); @@ -284,7 +271,7 @@ class Loop { * * If there's no more pending timers, this function returns null. * - * @return float + * @return float|null */ protected function runTimers() { @@ -295,7 +282,7 @@ class Loop { // Add the last timer back to the array. if ($timer) { $this->timers[] = $timer; - return $timer[0] - microtime(true); + return max(0, $timer[0] - microtime(true)); } } @@ -303,7 +290,10 @@ class Loop { /** * Runs all pending stream events. * - * @param float $timeout + * If $timeout is 0, it will return immediately. If $timeout is null, it + * will wait indefinitely. + * + * @param float|null timeout */ protected function runStreams($timeout) { @@ -312,7 +302,7 @@ class Loop { $read = $this->readStreams; $write = $this->writeStreams; $except = null; - if (stream_select($read, $write, $except, null, $timeout)) { + if (stream_select($read, $write, $except, ($timeout === null) ? null : 0, $timeout ? (int)($timeout * 1000000) : 0)) { // See PHP Bug https://bugs.php.net/bug.php?id=62452 // Fixed in PHP7 @@ -328,7 +318,7 @@ class Loop { } } elseif ($this->running && ($this->nextTick || $this->timers)) { - usleep($timeout !== null ? $timeout * 1000000 : 200000); + usleep($timeout !== null ? intval($timeout * 1000000) : 200000); } } diff --git a/htdocs/includes/sabre/sabre/event/lib/Loop/functions.php b/htdocs/includes/sabre/sabre/event/lib/Loop/functions.php index 56c5bc8c74a..b5884b2b62f 100644 --- a/htdocs/includes/sabre/sabre/event/lib/Loop/functions.php +++ b/htdocs/includes/sabre/sabre/event/lib/Loop/functions.php @@ -1,15 +1,13 @@ -setTimeout($cb, $timeout); @@ -20,24 +18,19 @@ function setTimeout(callable $cb, $timeout) { * * The value this function returns can be used to stop the interval with * clearInterval. - * - * @param callable $cb - * @param float $timeout - * @return array */ -function setInterval(callable $cb, $timeout) { +function setInterval(callable $cb, float $timeout) : array { return instance()->setInterval($cb, $timeout); } /** - * Stops a running internval. + * Stops a running interval. * - * @param array $intervalId * @return void */ -function clearInterval($intervalId) { +function clearInterval(array $intervalId) { instance()->clearInterval($intervalId); @@ -46,7 +39,6 @@ function clearInterval($intervalId) { /** * Runs a function immediately at the next iteration of the loop. * - * @param callable $cb * @return void */ function nextTick(callable $cb) { @@ -66,7 +58,6 @@ function nextTick(callable $cb) { * prevent the eventloop from never stopping. * * @param resource $stream - * @param callable $cb * @return void */ function addReadStream($stream, callable $cb) { @@ -85,7 +76,6 @@ function addReadStream($stream, callable $cb) { * prevent the eventloop from never stopping. * * @param resource $stream - * @param callable $cb * @return void */ function addWriteStream($stream, callable $cb) { @@ -144,11 +134,8 @@ function run() { * * This function will return true if there are _any_ events left in the * loop after the tick. - * - * @param bool $block - * @return bool */ -function tick($block = false) { +function tick(bool $block = false) : bool { return instance()->tick($block); @@ -167,10 +154,8 @@ function stop() { /** * Retrieves or sets the global Loop object. - * - * @param Loop $newLoop */ -function instance(Loop $newLoop = null) { +function instance(Loop $newLoop = null) : Loop { static $loop; if ($newLoop) { diff --git a/htdocs/includes/sabre/sabre/event/lib/Promise.php b/htdocs/includes/sabre/sabre/event/lib/Promise.php index 1c874c1bda3..1d04bd4d456 100644 --- a/htdocs/includes/sabre/sabre/event/lib/Promise.php +++ b/htdocs/includes/sabre/sabre/event/lib/Promise.php @@ -1,8 +1,9 @@ -fulfill and $this->reject. * Using the executor is optional. - * - * @param callable $executor */ function __construct(callable $executor = null) { @@ -84,12 +83,8 @@ class Promise { * * If either of the callbacks throw an exception, the returned promise will * be rejected and the exception will be passed back. - * - * @param callable $onFulfilled - * @param callable $onRejected - * @return Promise */ - function then(callable $onFulfilled = null, callable $onRejected = null) { + function then(callable $onFulfilled = null, callable $onRejected = null) : Promise { // This new subPromise will be returned from this function, and will // be fulfilled with the result of the onFulfilled or onRejected event @@ -122,11 +117,8 @@ class Promise { * * Its usage is identical to then(). However, the otherwise() function is * preferred. - * - * @param callable $onRejected - * @return Promise */ - function otherwise(callable $onRejected) { + function otherwise(callable $onRejected) : Promise { return $this->then(null, $onRejected); @@ -152,13 +144,9 @@ class Promise { /** * Marks this promise as rejected, and set it's rejection reason. * - * While it's possible to use any PHP value as the reason, it's highly - * recommended to use an Exception for this. - * - * @param mixed $reason * @return void */ - function reject($reason = null) { + function reject(Throwable $reason) { if ($this->state !== self::PENDING) { throw new PromiseAlreadyResolvedException('This promise is already resolved, and you\'re not allowed to resolve a promise more than once'); } @@ -181,7 +169,6 @@ class Promise { * one. In PHP it might be useful to call this on the last promise in a * chain. * - * @throws Exception * @return mixed */ function wait() { @@ -205,15 +192,7 @@ class Promise { } else { // If we got here, it means that the asynchronous operation // errored. Therefore we need to throw an exception. - $reason = $this->value; - if ($reason instanceof Exception) { - throw $reason; - } elseif (is_scalar($reason)) { - throw new Exception($reason); - } else { - $type = is_object($reason) ? get_class($reason) : gettype($reason); - throw new Exception('Promise was rejected with reason of type: ' . $type); - } + throw $this->value; } @@ -272,7 +251,7 @@ class Promise { // immediately fulfill the chained promise. $subPromise->fulfill($result); } - } catch (Exception $e) { + } catch (Throwable $e) { // If the event handler threw an exception, we need to make sure that // the chained promise is rejected as well. $subPromise->reject($e); @@ -287,34 +266,4 @@ class Promise { }); } - /** - * Alias for 'otherwise'. - * - * This function is now deprecated and will be removed in a future version. - * - * @param callable $onRejected - * @deprecated - * @return Promise - */ - function error(callable $onRejected) { - - return $this->otherwise($onRejected); - - } - - /** - * Deprecated. - * - * Please use Sabre\Event\Promise::all - * - * @param Promise[] $promises - * @deprecated - * @return Promise - */ - static function all(array $promises) { - - return Promise\all($promises); - - } - } diff --git a/htdocs/includes/sabre/sabre/event/lib/Promise/functions.php b/htdocs/includes/sabre/sabre/event/lib/Promise/functions.php index 3604b8aaa95..b846b7b84d1 100644 --- a/htdocs/includes/sabre/sabre/event/lib/Promise/functions.php +++ b/htdocs/includes/sabre/sabre/event/lib/Promise/functions.php @@ -1,14 +1,15 @@ -error( + )->otherwise( function($reason) use ($fail) { $fail($reason); } @@ -66,9 +66,8 @@ function all(array $promises) { * that first promise. * * @param Promise[] $promises - * @return Promise */ -function race(array $promises) { +function race(array $promises) : Promise { return new Promise(function($success, $fail) use ($promises) { @@ -106,9 +105,8 @@ function race(array $promises) { * promise and eventually get the same state as the followed promise. * * @param mixed $value - * @return Promise */ -function resolve($value) { +function resolve($value) : Promise { if ($value instanceof Promise) { return $value->then(); @@ -122,11 +120,8 @@ function resolve($value) { /** * Returns a Promise that will reject with the given reason. - * - * @param mixed $reason - * @return Promise */ -function reject($reason) { +function reject(Throwable $reason) : Promise { $promise = new Promise(); $promise->reject($reason); diff --git a/htdocs/includes/sabre/sabre/event/lib/PromiseAlreadyResolvedException.php b/htdocs/includes/sabre/sabre/event/lib/PromiseAlreadyResolvedException.php index 86a6c5b3f1a..534a3d49416 100644 --- a/htdocs/includes/sabre/sabre/event/lib/PromiseAlreadyResolvedException.php +++ b/htdocs/includes/sabre/sabre/event/lib/PromiseAlreadyResolvedException.php @@ -1,4 +1,4 @@ -wildcardListeners; + } else { + $listeners = & $this->listeners; + } + + // Always fully reset the listener index. This is fairly sane for most + // applications, because there's a clear "event registering" and "event + // emitting" phase, but can be slow if there's a lot adding and removing + // of listeners during emitting of events. + $this->listenerIndex = []; + + if (!isset($listeners[$eventName])) { + $listeners[$eventName] = []; + } + $listeners[$eventName][] = [$priority, $callBack]; + + } + + /** + * Subscribe to an event exactly once. + * + * @return void + */ + function once(string $eventName, callable $callBack, int $priority = 100) { + + $wrapper = null; + $wrapper = function() use ($eventName, $callBack, &$wrapper) { + + $this->removeListener($eventName, $wrapper); + return call_user_func_array($callBack, func_get_args()); + + }; + + $this->on($eventName, $wrapper, $priority); + + } + + /** + * Emits an event. + * + * This method will return true if 0 or more listeners were succesfully + * handled. false is returned if one of the events broke the event chain. + * + * If the continueCallBack is specified, this callback will be called every + * time before the next event handler is called. + * + * If the continueCallback returns false, event propagation stops. This + * allows you to use the eventEmitter as a means for listeners to implement + * functionality in your application, and break the event loop as soon as + * some condition is fulfilled. + * + * Note that returning false from an event subscriber breaks propagation + * and returns false, but if the continue-callback stops propagation, this + * is still considered a 'successful' operation and returns true. + * + * Lastly, if there are 5 event handlers for an event. The continueCallback + * will be called at most 4 times. + */ + function emit(string $eventName, array $arguments = [], callable $continueCallBack = null) : bool { + + if (is_null($continueCallBack)) { + + foreach ($this->listeners($eventName) as $listener) { + + $result = call_user_func_array($listener, $arguments); + if ($result === false) { + return false; + } + } + + } else { + + $listeners = $this->listeners($eventName); + $counter = count($listeners); + + foreach ($listeners as $listener) { + + $counter--; + $result = call_user_func_array($listener, $arguments); + if ($result === false) { + return false; + } + + if ($counter > 0) { + if (!$continueCallBack()) break; + } + + } + + } + + return true; + + + } + + /** + * Returns the list of listeners for an event. + * + * The list is returned as an array, and the list of events are sorted by + * their priority. + * + * @return callable[] + */ + function listeners(string $eventName) : array { + + if (!array_key_exists($eventName, $this->listenerIndex)) { + + // Create a new index. + $listeners = []; + $listenersPriority = []; + if (isset($this->listeners[$eventName])) foreach ($this->listeners[$eventName] as $listener) { + + $listenersPriority[] = $listener[0]; + $listeners[] = $listener[1]; + + } + + foreach ($this->wildcardListeners as $wcEvent => $wcListeners) { + + // Wildcard match + if (substr($eventName, 0, strlen($wcEvent)) === $wcEvent) { + + foreach ($wcListeners as $listener) { + + $listenersPriority[] = $listener[0]; + $listeners[] = $listener[1]; + + } + + } + + } + + // Sorting by priority + array_multisort($listenersPriority, SORT_NUMERIC, $listeners); + + // Creating index + $this->listenersIndex[$eventName] = $listeners; + + } + + return $this->listenersIndex[$eventName]; + + } + + /** + * Removes a specific listener from an event. + * + * If the listener could not be found, this method will return false. If it + * was removed it will return true. + */ + function removeListener(string $eventName, callable $listener) : bool { + + // If it ends with a wildcard, we use the wildcardListeners array + if ($eventName[strlen($eventName) - 1] === '*') { + $eventName = substr($eventName, 0, -1); + $listeners = & $this->wildcardListeners; + } else { + $listeners = & $this->listeners; + } + + if (!isset($listeners[$eventName])) { + return false; + } + + foreach ($listeners[$eventName] as $index => $check) { + + if ($check[1] === $listener) { + + // Remove listener + unset($listeners[$eventName][$index]); + // Reset index + $this->listenerIndex = []; + return true; + + } + + } + + return false; + + } + + /** + * Removes all listeners. + * + * If the eventName argument is specified, all listeners for that event are + * removed. If it is not specified, every listener for every event is + * removed. + * + * @return void + */ + function removeAllListeners(string $eventName = null) { + + if (is_null($eventName)) { + $this->listeners = []; + $this->wildcardListeners = []; + + } else { + + if ($eventName[strlen($eventName) - 1] === '*') { + // Wildcard event + unset($this->wildcardListeners[substr($eventName, 0, -1)]); + } else { + unset($this->listeners[$eventName]); + } + + } + + // Reset index + $this->listenerIndex = []; + + } + + /** + * The list of listeners + */ + protected $listeners = []; + + /** + * The list of "wildcard listeners". + */ + protected $wildcardListeners = []; + + /** + * An index of listeners for a specific event name. This helps speeding + * up emitting events after all listeners have been set. + * + * If the list of listeners changes though, the index clears. + */ + protected $listenerIndex = []; +} diff --git a/htdocs/includes/sabre/sabre/event/lib/coroutine.php b/htdocs/includes/sabre/sabre/event/lib/coroutine.php index 19c0ba8a714..f571a8ba0c9 100644 --- a/htdocs/includes/sabre/sabre/event/lib/coroutine.php +++ b/htdocs/includes/sabre/sabre/event/lib/coroutine.php @@ -1,9 +1,9 @@ -request('GET', '/foo'); * yield $httpClient->request('DELETE', /foo'); * yield $httpClient->request('PUT', '/foo'); - * } catch(\Exception $reason) { + * } catch(\Throwable $reason) { * echo "Failed because: $reason\n"; * } * * }); * - * @copyright Copyright (C) 2013-2015 fruux GmbH. All rights reserved. + * @return Sabre\Event\Promise + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ -function coroutine(callable $gen) { +function coroutine(callable $gen) : Promise { $generator = $gen(); if (!$generator instanceof Generator) { @@ -54,8 +55,6 @@ function coroutine(callable $gen) { // This is the value we're returning. $promise = new Promise(); - $lastYieldResult = null; - /** * So tempted to use the mythical y-combinator here, but it's not needed in * PHP. @@ -68,22 +67,14 @@ function coroutine(callable $gen) { if ($yieldedValue instanceof Promise) { $yieldedValue->then( function($value) use ($generator, &$advanceGenerator, &$lastYieldResult) { - $lastYieldResult = $value; $generator->send($value); $advanceGenerator(); }, - function($reason) use ($generator, $advanceGenerator) { - if ($reason instanceof Exception) { - $generator->throw($reason); - } elseif (is_scalar($reason)) { - $generator->throw(new Exception($reason)); - } else { - $type = is_object($reason) ? get_class($reason) : gettype($reason); - $generator->throw(new Exception('Promise was rejected with reason of type: ' . $type)); - } + function(Throwable $reason) use ($generator, $advanceGenerator) { + $generator->throw($reason); $advanceGenerator(); } - )->error(function($reason) use ($promise) { + )->otherwise(function(Throwable $reason) use ($promise) { // This error handler would be called, if something in the // generator throws an exception, and it's not caught // locally. @@ -94,24 +85,38 @@ function coroutine(callable $gen) { break; } else { // If the value was not a promise, we'll just let it pass through. - $lastYieldResult = $yieldedValue; $generator->send($yieldedValue); } } // If the generator is at the end, and we didn't run into an exception, - // we can fullfill the promise with the last thing that was yielded to - // us. - if (!$generator->valid() && $promise->state === Promise::PENDING) { - $promise->fulfill($lastYieldResult); + // We're grabbing the "return" value and fulfilling our top-level + // promise with its value. + if (!$generator->valid() && $promise->state === Promise::PENDING) { + $returnValue = $generator->getReturn(); + + // The return value is a promise. + if ($returnValue instanceof Promise) { + $returnValue->then(function($value) use ($promise) { + $promise->fulfill($value); + }, function(Throwable $reason) { + $promise->reject($reason); + }); + } else { + + $promise->fulfill($returnValue); + + } + + } }; try { $advanceGenerator(); - } catch (Exception $e) { + } catch (Throwable $e) { $promise->reject($e); } diff --git a/htdocs/includes/sabre/sabre/http/.gitignore b/htdocs/includes/sabre/sabre/http/.gitignore index 8c97686fb57..5f85ecc1b98 100644 --- a/htdocs/includes/sabre/sabre/http/.gitignore +++ b/htdocs/includes/sabre/sabre/http/.gitignore @@ -6,10 +6,13 @@ composer.lock tests/cov/ # Composer binaries -bin/phpunit -bin/phpcs -bin/php-cs-fixer -bin/sabre-cs-fixer +bin/phpunit* +bin/phpcs* +bin/php-cs-fixer* +bin/sabre-cs-fixer* # Vim .*.swp + +# development stuff +.php_cs.cache diff --git a/htdocs/includes/sabre/sabre/http/.php_cs.dist b/htdocs/includes/sabre/sabre/http/.php_cs.dist new file mode 100644 index 00000000000..8d61ee25924 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/.php_cs.dist @@ -0,0 +1,12 @@ +getFinder() + ->exclude('vendor') + ->in(__DIR__); +$config->setRules([ + '@PSR1' => true, + '@Symfony' =>true +]); + +return $config; \ No newline at end of file diff --git a/htdocs/includes/sabre/sabre/http/.travis.yml b/htdocs/includes/sabre/sabre/http/.travis.yml index 8ae84d90f8e..3b85d5f3913 100644 --- a/htdocs/includes/sabre/sabre/http/.travis.yml +++ b/htdocs/includes/sabre/sabre/http/.travis.yml @@ -1,26 +1,41 @@ language: php +sudo: required php: - - 5.4 - - 5.5 - - 5.6 - - 7 + - 7.0 - 7.1 - - hhvm - -matrix: - fast_finish: true + - 7.2 + - 7.3 + - 7.4snapshot env: + global: + - RUN_PHPSTAN="FALSE" matrix: - - PREFER_LOWEST="" - - PREFER_LOWEST="--prefer-lowest" + - PREFER_LOWEST="" WITH_COVERAGE="--coverage-clover=coverage.xml" + - PREFER_LOWEST="--prefer-lowest" $WITH_COVERAGE="" +matrix: + include: + - name: 'PHPStan' + php: 7.2 + env: RUN_PHPSTAN="TRUE" + fast_finish: true + +cache: + directories: + - $HOME/.composer/cache + +install: + - if [ $RUN_PHPSTAN == "TRUE" ]; then composer require --dev phpstan/phpstan; fi before_script: - - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini -# - composer self-update - - composer update --prefer-source $PREFER_LOWEST + - composer update --prefer-source $PREFER_LOWEST + - PHP_BIN=$(phpenv which php) + - sudo $PHP_BIN -S localhost:80 -t $TRAVIS_BUILD_DIR/tests/www 2>/dev/null & script: - - ./bin/phpunit --configuration tests/phpunit.xml - - ./bin/sabre-cs-fixer fix . --dry-run --diff + - if [ $RUN_PHPSTAN == "FALSE" ]; then ./bin/phpunit --configuration tests/phpunit.xml $WITH_COVERAGE; fi + - if [ $RUN_PHPSTAN == "TRUE" ]; then php ./bin/phpstan analyse -c phpstan.neon lib; fi + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/htdocs/includes/sabre/sabre/http/CHANGELOG.md b/htdocs/includes/sabre/sabre/http/CHANGELOG.md index 63d85afe35c..e300aad09bc 100644 --- a/htdocs/includes/sabre/sabre/http/CHANGELOG.md +++ b/htdocs/includes/sabre/sabre/http/CHANGELOG.md @@ -1,6 +1,75 @@ ChangeLog ========= +5.0.5 (2019-11-28) +------------------------- + +* #138: Fixed possible infinite loop (@dpakach, @vfreex, @phil-davis) +* #136: Improvement regex content-range (@ho4ho) + +5.0.4 (2019-10-08) +------------------------- + +* #133: Fix short Content-Range download - Regression from 5.0.3 (@phil-davis) + +5.0.3 (2019-10-08) +------------------------- + +* #119: Significantly improve file download speed by enabling mmap based stream_copy_to_stream (@vfreex) + +5.0.2 (2019-09-12) +------------------------- + +* #125: Fix Strict Error if Response Body Empty (@WorksDev, @phil-davis) + +5.0.1 (2019-09-11) +------------------------- + +* #121: fix "Trying to access array offset on value of type bool" in 7.4 (@remicollet) +* #115: Reduce memory footprint when parsing HTTP result (@Gasol) +* #114: Misc code improvements (@mrcnpdlk) +* #111, #118: Added phpstan analysis (@DeepDiver1975, @staabm) +* #107: Tested with php 7.3 (@DeepDiver1975) + + +5.0.0 (2018-06-04) +------------------------- + +* #99: Previous CURL opts are not persisted anymore (@christiaan) +* Final release + +5.0.0-alpha1 (2018-02-16) +------------------------- + +* Now requires PHP 7.0+. +* Supports sabre/event 4.x and 5.x +* Depends on sabre/uri 2. +* hhvm is no longer supported starting this release. +* #65: It's now possible to supply request/response bodies using a callback + functions. This allows very high-speed/low-memory responses to be created. + (@petrkotek). +* Strict typing is used every where this is applicable. +* Removed `URLUtil` class. It was deprecated a long time ago, and most of + its functions moved to the `sabre/uri` package. +* Removed `Util` class. Most of its functions moved to the `functions.php` + file. +* #68: The `$method` and `$uri` arguments when constructing a Request object + are now required. +* When `Sapi::getRequest()` is called, we default to setting the HTTP Method + to `CLI`. +* The HTTP response is now initialized with HTTP code `500` instead of `null`, + so if it's not changed, it will be emitted as 500. +* #69: Sending `charset="UTF-8"` on Basic authentication challenges per + [rfc7617][rfc7617]. +* #84: Added support for `SERVER_PROTOCOL HTTP/2.0` (@jens1o) + + +4.2.3 (2017-06-12) +------------------ + +* #74, #77: Work around 4GB file size limit at 32 Bit systems + + 4.2.2 (2017-01-02) ------------------ @@ -253,4 +322,5 @@ Before 2.0.0, this package was built-into SabreDAV, where it first appeared in January 2009. [psr-http]: https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md -[rfc-7240]: http://tools.ietf.org/html/rfc7240 +[rfc7240]: http://tools.ietf.org/html/rfc7240 +[rfc7617]: https://tools.ietf.org/html/rfc7617 diff --git a/htdocs/includes/sabre/sabre/http/README.md b/htdocs/includes/sabre/sabre/http/README.md index ae03a796e13..8159b4d4976 100644 --- a/htdocs/includes/sabre/sabre/http/README.md +++ b/htdocs/includes/sabre/sabre/http/README.md @@ -1,7 +1,7 @@ sabre/http ========== -This library provides a toolkit to make working with the HTTP protocol easier. +This library provides a toolkit to make working with the [HTTP protocol](https://tools.ietf.org/html/rfc2616) easier. Most PHP scripts run within a HTTP request but accessing information about the HTTP request is cumbersome at least. @@ -30,8 +30,9 @@ Build status | branch | status | | ------ | ------ | -| master | [![Build Status](https://travis-ci.org/fruux/sabre-http.svg?branch=master)](https://travis-ci.org/fruux/sabre-http) | -| 3.0 | [![Build Status](https://travis-ci.org/fruux/sabre-http.svg?branch=3.0)](https://travis-ci.org/fruux/sabre-http) | +| master | [![Build Status](https://travis-ci.org/sabre-io/http.svg?branch=master)](https://travis-ci.org/sabre-io/http) | +| 4.2 | [![Build Status](https://travis-ci.org/sabre-io/http.svg?branch=4.2)](https://travis-ci.org/sabre-io/http) | +| 3.0 | [![Build Status](https://travis-ci.org/sabre-io/http.svg?branch=3.0)](https://travis-ci.org/sabre-io/http) | Installation ------------ @@ -42,7 +43,7 @@ or edit a `composer.json` file, and make sure it contains something like this: ```json { "require" : { - "sabre/http" : "~3.0.0" + "sabre/http" : "~5.0.0" } } ``` @@ -571,7 +572,7 @@ function removeHeader($name); /** * Sets the HTTP version. * - * Should be 1.0 or 1.1. + * Should be 1.0, 1.1 or 2.0. * * @param string $version * @return void @@ -716,7 +717,7 @@ function removeHeader($name); /** * Sets the HTTP version. * - * Should be 1.0 or 1.1. + * Should be 1.0, 1.1 or 2.0. * * @param string $version * @return void diff --git a/htdocs/includes/sabre/sabre/http/composer.json b/htdocs/includes/sabre/sabre/http/composer.json index 507d5d28df0..851a08730ff 100644 --- a/htdocs/includes/sabre/sabre/http/composer.json +++ b/htdocs/includes/sabre/sabre/http/composer.json @@ -5,15 +5,15 @@ "homepage" : "https://github.com/fruux/sabre-http", "license" : "BSD-3-Clause", "require" : { - "php" : ">=5.4", + "php" : "^7.0", "ext-mbstring" : "*", "ext-ctype" : "*", - "sabre/event" : ">=1.0.0,<4.0.0", - "sabre/uri" : "~1.0" + "ext-curl" : "*", + "sabre/event" : ">=4.0 <6.0", + "sabre/uri" : "^2.0" }, "require-dev" : { - "phpunit/phpunit" : "~4.3", - "sabre/cs" : "~0.0.1" + "phpunit/phpunit" : "^6.0 || ^7.0" }, "suggest" : { "ext-curl" : " to make http requests with the Client class" diff --git a/htdocs/includes/sabre/sabre/http/examples/asyncclient.php b/htdocs/includes/sabre/sabre/http/examples/asyncclient.php index b399e1e4f7b..af98e253c8d 100644 --- a/htdocs/includes/sabre/sabre/http/examples/asyncclient.php +++ b/htdocs/includes/sabre/sabre/http/examples/asyncclient.php @@ -15,10 +15,9 @@ use Sabre\HTTP\Request; // Find the autoloader $paths = [ - __DIR__ . '/../vendor/autoload.php', - __DIR__ . '/../../../autoload.php', - __DIR__ . '/vendor/autoload.php', - + __DIR__.'/../vendor/autoload.php', + __DIR__.'/../../../autoload.php', + __DIR__.'/vendor/autoload.php', ]; foreach ($paths as $path) { @@ -32,29 +31,27 @@ foreach ($paths as $path) { $request = new Request('GET', 'http://localhost/'); $client = new Client(); -for ($i = 0; $i < 1000; $i++) { - +for ($i = 0; $i < 1000; ++$i) { echo "$i sending\n"; $client->sendAsync( $request, // This is the 'success' callback - function($response) use ($i) { - echo "$i -> " . $response->getStatus() . "\n"; + function ($response) use ($i) { + echo "$i -> ".$response->getStatus()."\n"; }, // This is the 'error' callback. It is called for general connection // problems (such as not being able to connect to a host, dns errors, // etc.) and also cases where a response was returned, but it had a // status code of 400 or higher. - function($error) use ($i) { - - if ($error['status'] === Client::STATUS_CURLERROR) { + function ($error) use ($i) { + if (Client::STATUS_CURLERROR === $error['status']) { // Curl errors - echo "$i -> curl error: " . $error['curl_errmsg'] . "\n"; + echo "$i -> curl error: ".$error['curl_errmsg']."\n"; } else { // HTTP errors - echo "$i -> " . $error['response']->getStatus() . "\n"; + echo "$i -> ".$error['response']->getStatus()."\n"; } } ); diff --git a/htdocs/includes/sabre/sabre/http/examples/basicauth.php b/htdocs/includes/sabre/sabre/http/examples/basicauth.php index 58bb7899268..9c13da840b4 100644 --- a/htdocs/includes/sabre/sabre/http/examples/basicauth.php +++ b/htdocs/includes/sabre/sabre/http/examples/basicauth.php @@ -2,14 +2,15 @@ /** * This example shows how to do Basic authentication. - * * + * *. + * * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ $userList = [ - "user1" => "password", - "user2" => "password", + 'user1' => 'password', + 'user2' => 'password', ]; use Sabre\HTTP\Auth; @@ -18,10 +19,9 @@ use Sabre\HTTP\Sapi; // Find the autoloader $paths = [ - __DIR__ . '/../vendor/autoload.php', - __DIR__ . '/../../../autoload.php', - __DIR__ . '/vendor/autoload.php', - + __DIR__.'/../vendor/autoload.php', + __DIR__.'/../../../autoload.php', + __DIR__.'/vendor/autoload.php', ]; foreach ($paths as $path) { @@ -34,21 +34,16 @@ foreach ($paths as $path) { $request = Sapi::getRequest(); $response = new Response(); -$basicAuth = new Auth\Basic("Locked down area", $request, $response); +$basicAuth = new Auth\Basic('Locked down area', $request, $response); if (!$userPass = $basicAuth->getCredentials()) { - // No username or password given $basicAuth->requireLogin(); - } elseif (!isset($userList[$userPass[0]]) || $userList[$userPass[0]] !== $userPass[1]) { - // Username or password are incorrect $basicAuth->requireLogin(); } else { - // Success ! $response->setBody('You are logged in!'); - } // Sending the response diff --git a/htdocs/includes/sabre/sabre/http/examples/client.php b/htdocs/includes/sabre/sabre/http/examples/client.php index d16c1651b6c..a7cbfc3b4e2 100644 --- a/htdocs/includes/sabre/sabre/http/examples/client.php +++ b/htdocs/includes/sabre/sabre/http/examples/client.php @@ -13,10 +13,9 @@ use Sabre\HTTP\Request; // Find the autoloader $paths = [ - __DIR__ . '/../vendor/autoload.php', - __DIR__ . '/../../../autoload.php', - __DIR__ . '/vendor/autoload.php', - + __DIR__.'/../vendor/autoload.php', + __DIR__.'/../../../autoload.php', + __DIR__.'/vendor/autoload.php', ]; foreach ($paths as $path) { @@ -35,4 +34,4 @@ $response = $client->send($request); echo "Response:\n"; -echo (string)$response; +echo (string) $response; diff --git a/htdocs/includes/sabre/sabre/http/examples/digestauth.php b/htdocs/includes/sabre/sabre/http/examples/digestauth.php index 30a5501eb56..5594306f510 100644 --- a/htdocs/includes/sabre/sabre/http/examples/digestauth.php +++ b/htdocs/includes/sabre/sabre/http/examples/digestauth.php @@ -2,14 +2,15 @@ /** * This example shows how to do Digest authentication. - * * + * *. + * * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). * @author Markus Staab * @license http://sabre.io/license/ Modified BSD License */ $userList = [ - "user1" => "password", - "user2" => "password", + 'user1' => 'password', + 'user2' => 'password', ]; use Sabre\HTTP\Auth; @@ -18,10 +19,9 @@ use Sabre\HTTP\Sapi; // Find the autoloader $paths = [ - __DIR__ . '/../vendor/autoload.php', - __DIR__ . '/../../../autoload.php', - __DIR__ . '/vendor/autoload.php', - + __DIR__.'/../vendor/autoload.php', + __DIR__.'/../../../autoload.php', + __DIR__.'/vendor/autoload.php', ]; foreach ($paths as $path) { @@ -34,22 +34,17 @@ foreach ($paths as $path) { $request = Sapi::getRequest(); $response = new Response(); -$digestAuth = new Auth\Digest("Locked down area", $request, $response); +$digestAuth = new Auth\Digest('Locked down area', $request, $response); $digestAuth->init(); if (!$userName = $digestAuth->getUsername()) { - // No username given $digestAuth->requireLogin(); - } elseif (!isset($userList[$userName]) || !$digestAuth->validatePassword($userList[$userName])) { - // Username or password are incorrect $digestAuth->requireLogin(); } else { - // Success ! $response->setBody('You are logged in!'); - } // Sending the response diff --git a/htdocs/includes/sabre/sabre/http/examples/reverseproxy.php b/htdocs/includes/sabre/sabre/http/examples/reverseproxy.php index 289e2b55198..ce51ad43d23 100644 --- a/htdocs/includes/sabre/sabre/http/examples/reverseproxy.php +++ b/htdocs/includes/sabre/sabre/http/examples/reverseproxy.php @@ -16,10 +16,9 @@ use Sabre\HTTP\Sapi; // Find the autoloader $paths = [ - __DIR__ . '/../vendor/autoload.php', - __DIR__ . '/../../../autoload.php', - __DIR__ . '/vendor/autoload.php', - + __DIR__.'/../vendor/autoload.php', + __DIR__.'/../../../autoload.php', + __DIR__.'/vendor/autoload.php', ]; foreach ($paths as $path) { @@ -29,7 +28,6 @@ foreach ($paths as $path) { } } - $request = Sapi::getRequest(); $request->setBaseUrl($myBaseUrl); @@ -39,7 +37,7 @@ $subRequest = clone $request; $subRequest->removeHeader('Host'); // Rewriting the url. -$subRequest->setUrl($remoteUrl . $request->getPath()); +$subRequest->setUrl($remoteUrl.$request->getPath()); $client = new Client(); diff --git a/htdocs/includes/sabre/sabre/http/examples/stringify.php b/htdocs/includes/sabre/sabre/http/examples/stringify.php index 9f56201af9e..96f7da84b31 100644 --- a/htdocs/includes/sabre/sabre/http/examples/stringify.php +++ b/htdocs/includes/sabre/sabre/http/examples/stringify.php @@ -15,10 +15,9 @@ use Sabre\HTTP\Response; // Find the autoloader $paths = [ - __DIR__ . '/../vendor/autoload.php', - __DIR__ . '/../../../autoload.php', - __DIR__ . '/vendor/autoload.php', - + __DIR__.'/../vendor/autoload.php', + __DIR__.'/../../../autoload.php', + __DIR__.'/vendor/autoload.php', ]; foreach ($paths as $path) { if (file_exists($path)) { @@ -29,8 +28,8 @@ foreach ($paths as $path) { $request = new Request('POST', '/foo'); $request->setHeaders([ - 'Host' => 'example.org', - 'Content-Type' => 'application/json' + 'Host' => 'example.org', + 'Content-Type' => 'application/json', ]); $request->setBody(json_encode(['foo' => 'bar'])); @@ -41,10 +40,10 @@ echo "\r\n\r\n"; $response = new Response(424); $response->setHeaders([ 'Content-Type' => 'text/plain', - 'Connection' => 'close', + 'Connection' => 'close', ]); -$response->setBody("ABORT! ABORT!"); +$response->setBody('ABORT! ABORT!'); echo $response; diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/AWS.php b/htdocs/includes/sabre/sabre/http/lib/Auth/AWS.php index 5e176646aaf..ffda3cf1570 100644 --- a/htdocs/includes/sabre/sabre/http/lib/Auth/AWS.php +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/AWS.php @@ -1,11 +1,13 @@ request->getHeader('Authorization'); + + if (null === $authHeader) { + $this->errorCode = self::ERR_NOAWSHEADER; + + return false; + } $authHeader = explode(' ', $authHeader); - if ($authHeader[0] != 'AWS' || !isset($authHeader[1])) { + if ('AWS' !== $authHeader[0] || !isset($authHeader[1])) { $this->errorCode = self::ERR_NOAWSHEADER; - return false; + + return false; } list($this->accessKey, $this->signature) = explode(':', $authHeader[1]); return true; - } /** - * Returns the username for the request - * - * @return string + * Returns the username for the request. */ - function getAccessKey() { - + public function getAccessKey(): string + { return $this->accessKey; - } /** - * Validates the signature based on the secretKey - * - * @param string $secretKey - * @return bool + * Validates the signature based on the secretKey. */ - function validate($secretKey) { - + public function validate(string $secretKey): bool + { $contentMD5 = $this->request->getHeader('Content-MD5'); if ($contentMD5) { @@ -93,57 +93,53 @@ class AWS extends AbstractAuth { $body = $this->request->getBody(); $this->request->setBody($body); - if ($contentMD5 != base64_encode(md5($body, true))) { + if ($contentMD5 !== base64_encode(md5((string) $body, true))) { // content-md5 header did not match md5 signature of body $this->errorCode = self::ERR_MD5CHECKSUMWRONG; + return false; } - } - if (!$requestDate = $this->request->getHeader('x-amz-date')) + if (!$requestDate = $this->request->getHeader('x-amz-date')) { $requestDate = $this->request->getHeader('Date'); + } - if (!$this->validateRFC2616Date($requestDate)) + if (!$this->validateRFC2616Date((string) $requestDate)) { return false; + } $amzHeaders = $this->getAmzHeaders(); $signature = base64_encode( $this->hmacsha1($secretKey, - $this->request->getMethod() . "\n" . - $contentMD5 . "\n" . - $this->request->getHeader('Content-type') . "\n" . - $requestDate . "\n" . - $amzHeaders . + $this->request->getMethod()."\n". + $contentMD5."\n". + $this->request->getHeader('Content-type')."\n". + $requestDate."\n". + $amzHeaders. $this->request->getUrl() ) ); - if ($this->signature != $signature) { - + if ($this->signature !== $signature) { $this->errorCode = self::ERR_INVALIDSIGNATURE; - return false; + return false; } return true; - } - /** - * Returns an HTTP 401 header, forcing login + * Returns an HTTP 401 header, forcing login. * * This should be called when username and password are incorrect, or not supplied at all - * - * @return void */ - function requireLogin() { - + public function requireLogin() + { $this->response->addHeader('WWW-Authenticate', 'AWS'); $this->response->setStatus(401); - } /** @@ -154,17 +150,15 @@ class AWS extends AbstractAuth { * * This function also makes sure the Date header is within 15 minutes of the operating * system date, to prevent replay attacks. - * - * @param string $dateHeader - * @return bool */ - protected function validateRFC2616Date($dateHeader) { - - $date = Util::parseHTTPDate($dateHeader); + protected function validateRFC2616Date(string $dateHeader): bool + { + $date = HTTP\parseDate($dateHeader); // Unknown format if (!$date) { $this->errorCode = self::ERR_INVALIDDATEFORMAT; + return false; } @@ -174,47 +168,40 @@ class AWS extends AbstractAuth { // We allow 15 minutes around the current date/time if ($date > $max || $date < $min) { $this->errorCode = self::ERR_REQUESTTIMESKEWED; + return false; } - return $date; - + return true; } /** - * Returns a list of AMZ headers - * - * @return string + * Returns a list of AMZ headers. */ - protected function getAmzHeaders() { - + protected function getAmzHeaders(): string + { $amzHeaders = []; $headers = $this->request->getHeaders(); foreach ($headers as $headerName => $headerValue) { - if (strpos(strtolower($headerName), 'x-amz-') === 0) { - $amzHeaders[strtolower($headerName)] = str_replace(["\r\n"], [' '], $headerValue[0]) . "\n"; + if (0 === strpos(strtolower($headerName), 'x-amz-')) { + $amzHeaders[strtolower($headerName)] = str_replace(["\r\n"], [' '], $headerValue[0])."\n"; } } ksort($amzHeaders); $headerStr = ''; foreach ($amzHeaders as $h => $v) { - $headerStr .= $h . ':' . $v; + $headerStr .= $h.':'.$v; } return $headerStr; - } /** - * Generates an HMAC-SHA1 signature - * - * @param string $key - * @param string $message - * @return string + * Generates an HMAC-SHA1 signature. */ - private function hmacsha1($key, $message) { - + private function hmacsha1(string $key, string $message): string + { if (function_exists('hash_hmac')) { return hash_hmac('sha1', $message, $key, true); } @@ -226,9 +213,8 @@ class AWS extends AbstractAuth { $key = str_pad($key, $blocksize, chr(0x00)); $ipad = str_repeat(chr(0x36), $blocksize); $opad = str_repeat(chr(0x5c), $blocksize); - $hmac = pack('H*', sha1(($key ^ $opad) . pack('H*', sha1(($key ^ $ipad) . $message)))); + $hmac = pack('H*', sha1(($key ^ $opad).pack('H*', sha1(($key ^ $ipad).$message)))); + return $hmac; - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/AbstractAuth.php b/htdocs/includes/sabre/sabre/http/lib/Auth/AbstractAuth.php index ae45b3ee22a..ada6bf0f0d3 100644 --- a/htdocs/includes/sabre/sabre/http/lib/Auth/AbstractAuth.php +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/AbstractAuth.php @@ -1,5 +1,7 @@ realm = $realm; $this->request = $request; $this->response = $response; - } /** * This method sends the needed HTTP header and statuscode (401) to force * the user to login. - * - * @return void */ - abstract function requireLogin(); + abstract public function requireLogin(); /** - * Returns the HTTP realm - * - * @return string + * Returns the HTTP realm. */ - function getRealm() { - + public function getRealm(): string + { return $this->realm; - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/Basic.php b/htdocs/includes/sabre/sabre/http/lib/Auth/Basic.php index 60633b95726..d04b4a81126 100644 --- a/htdocs/includes/sabre/sabre/http/lib/Auth/Basic.php +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/Basic.php @@ -1,5 +1,7 @@ request->getHeader('Authorization'); if (!$auth) { return null; } - if (strtolower(substr($auth, 0, 6)) !== 'basic ') { + if ('basic ' !== strtolower(substr($auth, 0, 6))) { return null; } @@ -44,20 +46,15 @@ class Basic extends AbstractAuth { } return $credentials; - } /** * This method sends the needed HTTP header and statuscode (401) to force * the user to login. - * - * @return void */ - function requireLogin() { - - $this->response->addHeader('WWW-Authenticate', 'Basic realm="' . $this->realm . '"'); + public function requireLogin() + { + $this->response->addHeader('WWW-Authenticate', 'Basic realm="'.$this->realm.'", charset="UTF-8"'); $this->response->setStatus(401); - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/Bearer.php b/htdocs/includes/sabre/sabre/http/lib/Auth/Bearer.php index eefdf11ee97..988bb29d2a6 100644 --- a/htdocs/includes/sabre/sabre/http/lib/Auth/Bearer.php +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/Bearer.php @@ -1,5 +1,7 @@ request->getHeader('Authorization'); if (!$auth) { return null; } - if (strtolower(substr($auth, 0, 7)) !== 'bearer ') { + if ('bearer ' !== strtolower(substr($auth, 0, 7))) { return null; } return substr($auth, 7); - } /** * This method sends the needed HTTP header and statuscode (401) to force * authentication. - * - * @return void */ - function requireLogin() { - - $this->response->addHeader('WWW-Authenticate', 'Bearer realm="' . $this->realm . '"'); + public function requireLogin() + { + $this->response->addHeader('WWW-Authenticate', 'Bearer realm="'.$this->realm.'"'); $this->response->setStatus(401); - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/Auth/Digest.php b/htdocs/includes/sabre/sabre/http/lib/Auth/Digest.php index 4b3f0746fc6..dd35a0b7448 100644 --- a/htdocs/includes/sabre/sabre/http/lib/Auth/Digest.php +++ b/htdocs/includes/sabre/sabre/http/lib/Auth/Digest.php @@ -1,12 +1,14 @@ nonce = uniqid(); $this->opaque = md5($realm); parent::__construct($realm, $request, $response); - } /** - * Gathers all information from the headers + * Gathers all information from the headers. * * This method needs to be called prior to anything else. - * - * @return void */ - function init() { - + public function init() + { $digest = $this->getDigest(); - $this->digestParts = $this->parseDigest($digest); - + $this->digestParts = $this->parseDigest((string) $digest); } /** @@ -78,115 +76,101 @@ class Digest extends AbstractAuth { * QOP_AUTHINT ensures integrity of the request body, but this is not * supported by most HTTP clients. QOP_AUTHINT also requires the entire * request body to be md5'ed, which can put strains on CPU and memory. - * - * @param int $qop - * @return void */ - function setQOP($qop) { - + public function setQOP(int $qop) + { $this->qop = $qop; - } /** * Validates the user. * * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); - * - * @param string $A1 - * @return bool */ - function validateA1($A1) { - + public function validateA1(string $A1): bool + { $this->A1 = $A1; - return $this->validate(); + return $this->validate(); } /** * Validates authentication through a password. The actual password must be provided here. * It is strongly recommended not store the password in plain-text and use validateA1 instead. - * - * @param string $password - * @return bool */ - function validatePassword($password) { + public function validatePassword(string $password): bool + { + $this->A1 = md5($this->digestParts['username'].':'.$this->realm.':'.$password); - $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); return $this->validate(); - } /** - * Returns the username for the request + * Returns the username for the request. + * Returns null if there were none. * - * @return string + * @return string|null */ - function getUsername() { - - return $this->digestParts['username']; - + public function getUsername() + { + return $this->digestParts['username'] ?? null; } /** - * Validates the digest challenge - * - * @return bool + * Validates the digest challenge. */ - protected function validate() { + protected function validate(): bool + { + if (!is_array($this->digestParts)) { + return false; + } - $A2 = $this->request->getMethod() . ':' . $this->digestParts['uri']; + $A2 = $this->request->getMethod().':'.$this->digestParts['uri']; - if ($this->digestParts['qop'] == 'auth-int') { + if ('auth-int' === $this->digestParts['qop']) { // Making sure we support this qop value - if (!($this->qop & self::QOP_AUTHINT)) return false; + if (!($this->qop & self::QOP_AUTHINT)) { + return false; + } // We need to add an md5 of the entire request body to the A2 part of the hash $body = $this->request->getBody($asString = true); $this->request->setBody($body); - $A2 .= ':' . md5($body); - } else { - - // We need to make sure we support this qop value - if (!($this->qop & self::QOP_AUTH)) return false; + $A2 .= ':'.md5($body); + } elseif (!($this->qop & self::QOP_AUTH)) { + return false; } $A2 = md5($A2); $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); - return $this->digestParts['response'] == $validResponse; - - + return $this->digestParts['response'] === $validResponse; } /** - * Returns an HTTP 401 header, forcing login + * Returns an HTTP 401 header, forcing login. * * This should be called when username and password are incorrect, or not supplied at all - * - * @return void */ - function requireLogin() { - + public function requireLogin() + { $qop = ''; switch ($this->qop) { - case self::QOP_AUTH : + case self::QOP_AUTH: $qop = 'auth'; break; - case self::QOP_AUTHINT : + case self::QOP_AUTHINT: $qop = 'auth-int'; break; - case self::QOP_AUTH | self::QOP_AUTHINT : + case self::QOP_AUTH | self::QOP_AUTHINT: $qop = 'auth,auth-int'; break; } - $this->response->addHeader('WWW-Authenticate', 'Digest realm="' . $this->realm . '",qop="' . $qop . '",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); + $this->response->addHeader('WWW-Authenticate', 'Digest realm="'.$this->realm.'",qop="'.$qop.'",nonce="'.$this->nonce.'",opaque="'.$this->opaque.'"'); $this->response->setStatus(401); - } - /** * This method returns the full digest string. * @@ -196,23 +180,20 @@ class Digest extends AbstractAuth { * * @return mixed */ - function getDigest() { - + public function getDigest() + { return $this->request->getHeader('Authorization'); - } - /** * Parses the different pieces of the digest string into an array. * * This method returns false if an incomplete digest was supplied * - * @param string $digest - * @return mixed + * @return bool|array */ - protected function parseDigest($digest) { - + protected function parseDigest(string $digest) + { // protect against missing data $needed_parts = ['nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1]; $data = []; @@ -220,12 +201,10 @@ class Digest extends AbstractAuth { preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); foreach ($matches as $m) { - $data[$m[1]] = $m[2] ? $m[2] : $m[3]; + $data[$m[1]] = $m[2] ?: $m[3]; unset($needed_parts[$m[1]]); } return $needed_parts ? false : $data; - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/Client.php b/htdocs/includes/sabre/sabre/http/lib/Client.php index 0810c4a25b1..48862e7da4f 100644 --- a/htdocs/includes/sabre/sabre/http/lib/Client.php +++ b/htdocs/includes/sabre/sabre/http/lib/Client.php @@ -1,5 +1,7 @@ curlSettings = [ CURLOPT_RETURNTRANSFER => true, - CURLOPT_HEADER => true, - CURLOPT_NOBODY => false, - CURLOPT_USERAGENT => 'sabre-http/' . Version::VERSION . ' (http://sabre.io/)', + CURLOPT_NOBODY => false, + CURLOPT_USERAGENT => 'sabre-http/'.Version::VERSION.' (http://sabre.io/)', ]; + if ($separatedHeaders) { + $this->curlSettings[CURLOPT_HEADERFUNCTION] = [$this, 'receiveCurlHeader']; + } else { + $this->curlSettings[CURLOPT_HEADER] = true; + } + } + protected function receiveCurlHeader($curlHandle, $headerLine) + { + $this->headerLinesMap[(int) $curlHandle][] = $headerLine; + + return strlen($headerLine); } /** * Sends a request to a HTTP server, and returns a response. - * - * @param RequestInterface $request - * @return ResponseInterface */ - function send(RequestInterface $request) { - + public function send(RequestInterface $request): ResponseInterface + { $this->emit('beforeRequest', [$request]); $retryCount = 0; $redirects = 0; do { - $doRedirect = false; $retry = false; try { - $response = $this->doRequest($request); - $code = (int)$response->getStatus(); + $code = $response->getStatus(); // We are doing in-PHP redirects, because curl's // FOLLOW_LOCATION throws errors when PHP is configured with // open_basedir. // // https://github.com/fruux/sabre-http/issues/12 - if (in_array($code, [301, 302, 307, 308]) && $redirects < $this->maxRedirects) { - + if ($redirects < $this->maxRedirects && in_array($code, [301, 302, 307, 308])) { $oldLocation = $request->getUrl(); // Creating a new instance of the request object. @@ -123,20 +133,15 @@ class Client extends EventEmitter { )); $doRedirect = true; - $redirects++; - + ++$redirects; } // This was a HTTP error if ($code >= 400) { - $this->emit('error', [$request, $response, &$retry, $retryCount]); - $this->emit('error:' . $code, [$request, $response, &$retry, $retryCount]); - + $this->emit('error:'.$code, [$request, $response, &$retry, $retryCount]); } - } catch (ClientException $e) { - $this->emit('exception', [$request, $e, &$retry, $retryCount]); // If retry was still set to false, it means no event handler @@ -145,13 +150,11 @@ class Client extends EventEmitter { if (!$retry) { throw $e; } - } if ($retry) { - $retryCount++; + ++$retryCount; } - } while ($retry || $doRedirect); $this->emit('afterRequest', [$request, $response]); @@ -161,7 +164,6 @@ class Client extends EventEmitter { } return $response; - } /** @@ -172,32 +174,23 @@ class Client extends EventEmitter { * * After calling sendAsync, you must therefore occasionally call the poll() * method, or wait(). - * - * @param RequestInterface $request - * @param callable $success - * @param callable $error - * @return void */ - function sendAsync(RequestInterface $request, callable $success = null, callable $error = null) { - + public function sendAsync(RequestInterface $request, callable $success = null, callable $error = null) + { $this->emit('beforeRequest', [$request]); $this->sendAsyncInternal($request, $success, $error); $this->poll(); - } - /** * This method checks if any http requests have gotten results, and if so, * call the appropriate success or error handlers. * * This method will return true if there are still requests waiting to * return, and false if all the work is done. - * - * @return bool */ - function poll() { - + public function poll(): bool + { // nothing to do? if (!$this->curlMultiMap) { return false; @@ -208,10 +201,10 @@ class Client extends EventEmitter { $this->curlMultiHandle, $stillRunning ); - } while ($r === CURLM_CALL_MULTI_PERFORM); + } while (CURLM_CALL_MULTI_PERFORM === $r); + $messagesInQueue = 0; do { - messageQueue: $status = curl_multi_info_read( @@ -219,26 +212,25 @@ class Client extends EventEmitter { $messagesInQueue ); - if ($status && $status['msg'] === CURLMSG_DONE) { - - $resourceId = intval($status['handle']); + if ($status && CURLMSG_DONE === $status['msg']) { + $resourceId = (int) $status['handle']; list( $request, $successCallback, $errorCallback, - $retryCount, - ) = $this->curlMultiMap[$resourceId]; + $retryCount) = $this->curlMultiMap[$resourceId]; unset($this->curlMultiMap[$resourceId]); - $curlResult = $this->parseCurlResult(curl_multi_getcontent($status['handle']), $status['handle']); + + $curlHandle = $status['handle']; + $curlResult = $this->parseResponse(curl_multi_getcontent($curlHandle), $curlHandle); $retry = false; - if ($curlResult['status'] === self::STATUS_CURLERROR) { - + if (self::STATUS_CURLERROR === $curlResult['status']) { $e = new ClientException($curlResult['curl_errmsg'], $curlResult['curl_errno']); $this->emit('exception', [$request, $e, &$retry, $retryCount]); if ($retry) { - $retryCount++; + ++$retryCount; $this->sendAsyncInternal($request, $successCallback, $errorCallback, $retryCount); goto messageQueue; } @@ -248,18 +240,14 @@ class Client extends EventEmitter { if ($errorCallback) { $errorCallback($curlResult); } - - } elseif ($curlResult['status'] === self::STATUS_HTTPERROR) { - + } elseif (self::STATUS_HTTPERROR === $curlResult['status']) { $this->emit('error', [$request, $curlResult['response'], &$retry, $retryCount]); - $this->emit('error:' . $curlResult['http_code'], [$request, $curlResult['response'], &$retry, $retryCount]); + $this->emit('error:'.$curlResult['http_code'], [$request, $curlResult['response'], &$retry, $retryCount]); if ($retry) { - - $retryCount++; + ++$retryCount; $this->sendAsyncInternal($request, $successCallback, $errorCallback, $retryCount); goto messageQueue; - } $curlResult['request'] = $request; @@ -267,37 +255,29 @@ class Client extends EventEmitter { if ($errorCallback) { $errorCallback($curlResult); } - } else { - $this->emit('afterRequest', [$request, $curlResult['response']]); if ($successCallback) { $successCallback($curlResult['response']); } - } } - } while ($messagesInQueue > 0); return count($this->curlMultiMap) > 0; - } /** * Processes every HTTP request in the queue, and waits till they are all * completed. - * - * @return void */ - function wait() { - + public function wait() + { do { curl_multi_select($this->curlMultiHandle); $stillRunning = $this->poll(); } while ($stillRunning); - } /** @@ -309,14 +289,10 @@ class Client extends EventEmitter { * * This only works for the send() method. Throwing exceptions for * sendAsync() is not supported. - * - * @param bool $throwExceptions - * @return void */ - function setThrowExceptions($throwExceptions) { - + public function setThrowExceptions(bool $throwExceptions) + { $this->throwExceptions = $throwExceptions; - } /** @@ -324,40 +300,34 @@ class Client extends EventEmitter { * * These settings will be included in every HTTP request. * - * @param int $name * @param mixed $value - * @return void */ - function addCurlSetting($name, $value) { - + public function addCurlSetting(int $name, $value) + { $this->curlSettings[$name] = $value; - } /** * This method is responsible for performing a single request. - * - * @param RequestInterface $request - * @return ResponseInterface */ - protected function doRequest(RequestInterface $request) { - + protected function doRequest(RequestInterface $request): ResponseInterface + { $settings = $this->createCurlSettingsArray($request); if (!$this->curlHandle) { $this->curlHandle = curl_init(); + } else { + curl_reset($this->curlHandle); } curl_setopt_array($this->curlHandle, $settings); $response = $this->curlExec($this->curlHandle); - $response = $this->parseCurlResult($response, $this->curlHandle); - - if ($response['status'] === self::STATUS_CURLERROR) { + $response = $this->parseResponse($response, $this->curlHandle); + if (self::STATUS_CURLERROR === $response['status']) { throw new ClientException($response['curl_errmsg'], $response['curl_errno']); } return $response['response']; - } /** @@ -389,28 +359,21 @@ class Client extends EventEmitter { /** * Turns a RequestInterface object into an array with settings that can be - * fed to curl_setopt - * - * @param RequestInterface $request - * @return array + * fed to curl_setopt. */ - protected function createCurlSettingsArray(RequestInterface $request) { - + protected function createCurlSettingsArray(RequestInterface $request): array + { $settings = $this->curlSettings; switch ($request->getMethod()) { - case 'HEAD' : + case 'HEAD': $settings[CURLOPT_NOBODY] = true; $settings[CURLOPT_CUSTOMREQUEST] = 'HEAD'; - $settings[CURLOPT_POSTFIELDS] = ''; - $settings[CURLOPT_PUT] = false; break; - case 'GET' : + case 'GET': $settings[CURLOPT_CUSTOMREQUEST] = 'GET'; - $settings[CURLOPT_POSTFIELDS] = ''; - $settings[CURLOPT_PUT] = false; break; - default : + default: $body = $request->getBody(); if (is_resource($body)) { // This needs to be set to PUT, regardless of the actual @@ -422,20 +385,17 @@ class Client extends EventEmitter { // For security we cast this to a string. If somehow an array could // be passed here, it would be possible for an attacker to use @ to // post local files. - $settings[CURLOPT_POSTFIELDS] = (string)$body; + $settings[CURLOPT_POSTFIELDS] = (string) $body; } $settings[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); break; - } $nHeaders = []; foreach ($request->getHeaders() as $key => $values) { - foreach ($values as $value) { - $nHeaders[] = $key . ': ' . $value; + $nHeaders[] = $key.': '.$value; } - } $settings[CURLOPT_HTTPHEADER] = $nHeaders; $settings[CURLOPT_URL] = $request->getUrl(); @@ -449,13 +409,32 @@ class Client extends EventEmitter { } return $settings; - } const STATUS_SUCCESS = 0; const STATUS_CURLERROR = 1; const STATUS_HTTPERROR = 2; + private function parseResponse(string $response, $curlHandle): array + { + $settings = $this->curlSettings; + $separatedHeaders = isset($settings[CURLOPT_HEADERFUNCTION]) && (bool) $settings[CURLOPT_HEADERFUNCTION]; + + if ($separatedHeaders) { + $resourceId = (int) $curlHandle; + if (isset($this->headerLinesMap[$resourceId])) { + $headers = $this->headerLinesMap[$resourceId]; + } else { + $headers = []; + } + $response = $this->parseCurlResponse($headers, $response, $curlHandle); + } else { + $response = $this->parseCurlResult($response, $curlHandle); + } + + return $response; + } + /** * Parses the result of a curl call in a format that's a bit more * convenient to work with. @@ -471,12 +450,12 @@ class Client extends EventEmitter { * * http_code - HTTP status code, as an int. Only set if Only set if * status is STATUS_SUCCESS, or STATUS_HTTPERROR * - * @param string $response + * @param array $headerLines + * @param string $body * @param resource $curlHandle - * @return Response */ - protected function parseCurlResult($response, $curlHandle) { - + protected function parseCurlResponse(array $headerLines, string $body, $curlHandle): array + { list( $curlInfo, $curlErrNo, @@ -485,8 +464,63 @@ class Client extends EventEmitter { if ($curlErrNo) { return [ - 'status' => self::STATUS_CURLERROR, - 'curl_errno' => $curlErrNo, + 'status' => self::STATUS_CURLERROR, + 'curl_errno' => $curlErrNo, + 'curl_errmsg' => $curlErrMsg, + ]; + } + + $response = new Response(); + $response->setStatus($curlInfo['http_code']); + $response->setBody($body); + + foreach ($headerLines as $header) { + $parts = explode(':', $header, 2); + if (2 === count($parts)) { + $response->addHeader(trim($parts[0]), trim($parts[1])); + } + } + + $httpCode = $response->getStatus(); + + return [ + 'status' => $httpCode >= 400 ? self::STATUS_HTTPERROR : self::STATUS_SUCCESS, + 'response' => $response, + 'http_code' => $httpCode, + ]; + } + + /** + * Parses the result of a curl call in a format that's a bit more + * convenient to work with. + * + * The method returns an array with the following elements: + * * status - one of the 3 STATUS constants. + * * curl_errno - A curl error number. Only set if status is + * STATUS_CURLERROR. + * * curl_errmsg - A current error message. Only set if status is + * STATUS_CURLERROR. + * * response - Response object. Only set if status is STATUS_SUCCESS, or + * STATUS_HTTPERROR. + * * http_code - HTTP status code, as an int. Only set if Only set if + * status is STATUS_SUCCESS, or STATUS_HTTPERROR + * + * @deprecated Use parseCurlResponse instead + * + * @param resource $curlHandle + */ + protected function parseCurlResult(string $response, $curlHandle): array + { + list( + $curlInfo, + $curlErrNo, + $curlErrMsg + ) = $this->curlStuff($curlHandle); + + if ($curlErrNo) { + return [ + 'status' => self::STATUS_CURLERROR, + 'curl_errno' => $curlErrNo, 'curl_errmsg' => $curlErrMsg, ]; } @@ -495,7 +529,7 @@ class Client extends EventEmitter { // In the case of 204 No Content, strlen($response) == $curlInfo['header_size]. // This will cause substr($response, $curlInfo['header_size']) return FALSE instead of NULL // An exception will be thrown when calling getBodyAsString then - $responseBody = substr($response, $curlInfo['header_size']) ?: null; + $responseBody = substr($response, $curlInfo['header_size']) ?: ''; unset($response); @@ -510,26 +544,7 @@ class Client extends EventEmitter { // Splitting headers $headerBlob = explode("\r\n", $headerBlob); - $response = new Response(); - $response->setStatus($curlInfo['http_code']); - - foreach ($headerBlob as $header) { - $parts = explode(':', $header, 2); - if (count($parts) == 2) { - $response->addHeader(trim($parts[0]), trim($parts[1])); - } - } - - $response->setBody($responseBody); - - $httpCode = intval($response->getStatus()); - - return [ - 'status' => $httpCode >= 400 ? self::STATUS_HTTPERROR : self::STATUS_SUCCESS, - 'response' => $response, - 'http_code' => $httpCode, - ]; - + return $this->parseCurlResponse($headerBlob, $responseBody, $curlHandle); } /** @@ -537,14 +552,9 @@ class Client extends EventEmitter { * * We keep this in a separate method, so we can call it without triggering * the beforeRequest event and don't do the poll(). - * - * @param RequestInterface $request - * @param callable $success - * @param callable $error - * @param int $retryCount */ - protected function sendAsyncInternal(RequestInterface $request, callable $success, callable $error, $retryCount = 0) { - + protected function sendAsyncInternal(RequestInterface $request, callable $success, callable $error, int $retryCount = 0) + { if (!$this->curlMultiHandle) { $this->curlMultiHandle = curl_multi_init(); } @@ -554,29 +564,36 @@ class Client extends EventEmitter { $this->createCurlSettingsArray($request) ); curl_multi_add_handle($this->curlMultiHandle, $curl); - $this->curlMultiMap[intval($curl)] = [ + + $resourceId = (int) $curl; + $this->headerLinesMap[$resourceId] = []; + $this->curlMultiMap[$resourceId] = [ $request, $success, $error, - $retryCount + $retryCount, ]; - } // @codeCoverageIgnoreStart /** - * Calls curl_exec + * Calls curl_exec. * * This method exists so it can easily be overridden and mocked. * * @param resource $curlHandle - * @return string */ - protected function curlExec($curlHandle) { + protected function curlExec($curlHandle): string + { + $this->headerLinesMap[(int) $curlHandle] = []; - return curl_exec($curlHandle); + $result = curl_exec($curlHandle); + if (false === $result) { + $result = ''; + } + return $result; } /** @@ -585,17 +602,15 @@ class Client extends EventEmitter { * This method exists so it can easily be overridden and mocked. * * @param resource $curlHandle - * @return array */ - protected function curlStuff($curlHandle) { - + protected function curlStuff($curlHandle): array + { return [ curl_getinfo($curlHandle), curl_errno($curlHandle), curl_error($curlHandle), ]; - } - // @codeCoverageIgnoreEnd + // @codeCoverageIgnoreEnd } diff --git a/htdocs/includes/sabre/sabre/http/lib/ClientException.php b/htdocs/includes/sabre/sabre/http/lib/ClientException.php index 69631f44eef..2ca4a28e013 100644 --- a/htdocs/includes/sabre/sabre/http/lib/ClientException.php +++ b/htdocs/includes/sabre/sabre/http/lib/ClientException.php @@ -1,5 +1,7 @@ response = $response; parent::__construct($response->getStatusText(), $response->getStatus()); - } /** * The http status code for the error. - * - * @return int */ - function getHttpStatus() { - + public function getHttpStatus(): int + { return $this->response->getStatus(); - } /** * Returns the full response object. - * - * @return ResponseInterface */ - function getResponse() { - + public function getResponse(): ResponseInterface + { return $this->response; - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/HttpException.php b/htdocs/includes/sabre/sabre/http/lib/HttpException.php index 1303dec970e..80b3ae66575 100644 --- a/htdocs/includes/sabre/sabre/http/lib/HttpException.php +++ b/htdocs/includes/sabre/sabre/http/lib/HttpException.php @@ -1,5 +1,7 @@ getBody(); - if (is_string($body) || is_null($body)) { + if (is_callable($this->body)) { + $body = $this->getBodyAsString(); + } + if (is_string($body) || null === $body) { $stream = fopen('php://temp', 'r+'); - fwrite($stream, $body); + fwrite($stream, (string) $body); rewind($stream); + return $stream; } - return $body; + return $body; } /** @@ -62,77 +68,76 @@ abstract class Message implements MessageInterface { * * Note that because the underlying data may be based on a stream, this * method could only work correctly the first time. - * - * @return string */ - function getBodyAsString() { - + public function getBodyAsString(): string + { $body = $this->getBody(); if (is_string($body)) { return $body; } - if (is_null($body)) { + if (null === $body) { return ''; } + if (is_callable($body)) { + ob_start(); + $body(); + + return ob_get_clean(); + } + /** + * @var string|int|null + */ $contentLength = $this->getHeader('Content-Length'); if (is_int($contentLength) || ctype_digit($contentLength)) { - return stream_get_contents($body, $contentLength); - } else { - return stream_get_contents($body); + return stream_get_contents($body, (int) $contentLength); } + + return stream_get_contents($body); } /** * Returns the message body, as it's internal representation. * - * This could be either a string or a stream. + * This could be either a string, a stream or a callback writing the body to php://output. * - * @return resource|string + * @return resource|string|callable */ - function getBody() { - + public function getBody() + { return $this->body; - } /** - * Replaces the body resource with a new stream or string. + * Replaces the body resource with a new stream, string or a callback writing the body to php://output. * - * @param resource|string $body + * @param resource|string|callable $body */ - function setBody($body) { - + public function setBody($body) + { $this->body = $body; - } /** * Returns all the HTTP headers as an array. * * Every header is returned as an array, with one or more values. - * - * @return array */ - function getHeaders() { - + public function getHeaders(): array + { $result = []; foreach ($this->headers as $headerInfo) { $result[$headerInfo[0]] = $headerInfo[1]; } - return $result; + return $result; } /** * Will return true or false, depending on if a HTTP header exists. - * - * @param string $name - * @return bool */ - function hasHeader($name) { - + public function hasHeader(string $name): bool + { return isset($this->headers[strtolower($name)]); - } /** @@ -148,18 +153,17 @@ abstract class Message implements MessageInterface { * `Set-Cookie` cannot be logically combined with a comma. In those cases * you *should* use getHeaderAsArray(). * - * @param string $name * @return string|null */ - function getHeader($name) { - + public function getHeader(string $name) + { $name = strtolower($name); if (isset($this->headers[$name])) { return implode(',', $this->headers[$name][1]); } - return null; + return null; } /** @@ -170,11 +174,10 @@ abstract class Message implements MessageInterface { * * If the header did not exists, this method will return an empty array. * - * @param string $name * @return string[] */ - function getHeaderAsArray($name) { - + public function getHeaderAsArray(string $name): array + { $name = strtolower($name); if (isset($this->headers[$name])) { @@ -182,7 +185,6 @@ abstract class Message implements MessageInterface { } return []; - } /** @@ -192,14 +194,11 @@ abstract class Message implements MessageInterface { * * If the header already existed, it will be overwritten. * - * @param string $name * @param string|string[] $value - * @return void */ - function setHeader($name, $value) { - - $this->headers[strtolower($name)] = [$name, (array)$value]; - + public function setHeader(string $name, $value) + { + $this->headers[strtolower($name)] = [$name, (array) $value]; } /** @@ -209,16 +208,12 @@ abstract class Message implements MessageInterface { * should be specified as either a string or an array. * * Any header that already existed will be overwritten. - * - * @param array $headers - * @return void */ - function setHeaders(array $headers) { - + public function setHeaders(array $headers) + { foreach ($headers as $name => $value) { $this->setHeader($name, $value); } - } /** @@ -228,77 +223,62 @@ abstract class Message implements MessageInterface { * another value. Individual values can be retrieved with * getHeadersAsArray. * - * @param string $name - * @param string $value - * @return void + * @param string|string[] $value */ - function addHeader($name, $value) { - + public function addHeader(string $name, $value) + { $lName = strtolower($name); if (isset($this->headers[$lName])) { $this->headers[$lName][1] = array_merge( $this->headers[$lName][1], - (array)$value + (array) $value ); } else { $this->headers[$lName] = [ $name, - (array)$value + (array) $value, ]; } - } /** * Adds a new set of HTTP headers. * * Any existing headers will not be overwritten. - * - * @param array $headers - * @return void */ - function addHeaders(array $headers) { - + public function addHeaders(array $headers) + { foreach ($headers as $name => $value) { $this->addHeader($name, $value); } - } - /** * Removes a HTTP header. * * The specified header name must be treated as case-insensitive. * This method should return true if the header was successfully deleted, * and false if the header did not exist. - * - * @param string $name - * @return bool */ - function removeHeader($name) { - + public function removeHeader(string $name): bool + { $name = strtolower($name); if (!isset($this->headers[$name])) { return false; } unset($this->headers[$name]); - return true; + return true; } /** * Sets the HTTP version. * - * Should be 1.0 or 1.1. - * - * @param string $version - * @return void + * Should be 1.0, 1.1 or 2.0. */ - function setHttpVersion($version) { - + public function setHttpVersion(string $version) + { $this->httpVersion = $version; - } /** @@ -306,9 +286,8 @@ abstract class Message implements MessageInterface { * * @return string */ - function getHttpVersion() { - + public function getHttpVersion(): string + { return $this->httpVersion; - } } diff --git a/htdocs/includes/sabre/sabre/http/lib/MessageDecoratorTrait.php b/htdocs/includes/sabre/sabre/http/lib/MessageDecoratorTrait.php index 1cb32da2259..d5504ac7895 100644 --- a/htdocs/includes/sabre/sabre/http/lib/MessageDecoratorTrait.php +++ b/htdocs/includes/sabre/sabre/http/lib/MessageDecoratorTrait.php @@ -1,5 +1,7 @@ inner->getBodyAsStream(); - } /** @@ -43,13 +44,10 @@ trait MessageDecoratorTrait { * * Note that because the underlying data may be based on a stream, this * method could only work correctly the first time. - * - * @return string */ - function getBodyAsString() { - + public function getBodyAsString(): string + { return $this->inner->getBodyAsString(); - } /** @@ -59,47 +57,37 @@ trait MessageDecoratorTrait { * * @return resource|string */ - function getBody() { - + public function getBody() + { return $this->inner->getBody(); - } /** * Updates the body resource with a new stream. * - * @param resource $body - * @return void + * @param resource|string|callable $body */ - function setBody($body) { - + public function setBody($body) + { $this->inner->setBody($body); - } /** * Returns all the HTTP headers as an array. * * Every header is returned as an array, with one or more values. - * - * @return array */ - function getHeaders() { - + public function getHeaders(): array + { return $this->inner->getHeaders(); - } /** * Will return true or false, depending on if a HTTP header exists. - * - * @param string $name - * @return bool */ - function hasHeader($name) { - + public function hasHeader(string $name): bool + { return $this->inner->hasHeader($name); - } /** @@ -115,13 +103,11 @@ trait MessageDecoratorTrait { * `Set-Cookie` cannot be logically combined with a comma. In those cases * you *should* use getHeaderAsArray(). * - * @param string $name * @return string|null */ - function getHeader($name) { - + public function getHeader(string $name) + { return $this->inner->getHeader($name); - } /** @@ -131,14 +117,10 @@ trait MessageDecoratorTrait { * item will appear in the array. * * If the header did not exists, this method will return an empty array. - * - * @param string $name - * @return string[] */ - function getHeaderAsArray($name) { - + public function getHeaderAsArray(string $name): array + { return $this->inner->getHeaderAsArray($name); - } /** @@ -148,14 +130,11 @@ trait MessageDecoratorTrait { * * If the header already existed, it will be overwritten. * - * @param string $name * @param string|string[] $value - * @return void */ - function setHeader($name, $value) { - + public function setHeader(string $name, $value) + { $this->inner->setHeader($name, $value); - } /** @@ -165,14 +144,10 @@ trait MessageDecoratorTrait { * should be specified as either a string or an array. * * Any header that already existed will be overwritten. - * - * @param array $headers - * @return void */ - function setHeaders(array $headers) { - + public function setHeaders(array $headers) + { $this->inner->setHeaders($headers); - } /** @@ -182,31 +157,23 @@ trait MessageDecoratorTrait { * another value. Individual values can be retrieved with * getHeadersAsArray. * - * @param string $name - * @param string $value - * @return void + * @param string|string[] $value */ - function addHeader($name, $value) { - + public function addHeader(string $name, $value) + { $this->inner->addHeader($name, $value); - } /** * Adds a new set of HTTP headers. * * Any existing headers will not be overwritten. - * - * @param array $headers - * @return void */ - function addHeaders(array $headers) { - + public function addHeaders(array $headers) + { $this->inner->addHeaders($headers); - } - /** * Removes a HTTP header. * @@ -214,38 +181,28 @@ trait MessageDecoratorTrait { * This method should return true if the header was successfully deleted, * and false if the header did not exist. * - * @param string $name * @return bool */ - function removeHeader($name) { - + public function removeHeader(string $name): bool + { return $this->inner->removeHeader($name); - } /** * Sets the HTTP version. * - * Should be 1.0 or 1.1. - * - * @param string $version - * @return void + * Should be 1.0, 1.1 or 2.0. */ - function setHttpVersion($version) { - + public function setHttpVersion(string $version) + { $this->inner->setHttpVersion($version); - } /** * Returns the HTTP version. - * - * @return string */ - function getHttpVersion() { - + public function getHttpVersion(): string + { return $this->inner->getHttpVersion(); - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/MessageInterface.php b/htdocs/includes/sabre/sabre/http/lib/MessageInterface.php index df55beb2ffe..8070845d995 100644 --- a/htdocs/includes/sabre/sabre/http/lib/MessageInterface.php +++ b/htdocs/includes/sabre/sabre/http/lib/MessageInterface.php @@ -1,5 +1,7 @@ setMethod($method); - if (!is_null($url)) $this->setUrl($url); - if (!is_null($headers)) $this->setHeaders($headers); - if (!is_null($body)) $this->setBody($body); - + public function __construct(string $method, string $url, array $headers = [], $body = null) + { + $this->setMethod($method); + $this->setUrl($url); + $this->setHeaders($headers); + $this->setBody($body); } /** - * Returns the current HTTP method - * - * @return string + * Returns the current HTTP method. */ - function getMethod() { - + public function getMethod(): string + { return $this->method; - } /** - * Sets the HTTP method - * - * @param string $method - * @return void + * Sets the HTTP method. */ - function setMethod($method) { - + public function setMethod(string $method) + { $this->method = $method; - } /** * Returns the request url. - * - * @return string */ - function getUrl() { - + public function getUrl(): string + { return $this->url; - } /** * Sets the request url. - * - * @param string $url - * @return void */ - function setUrl($url) { - + public function setUrl(string $url) + { $this->url = $url; - } /** * Returns the list of query parameters. * * This is equivalent to PHP's $_GET superglobal. - * - * @return array */ - function getQueryParameters() { - + public function getQueryParameters(): array + { $url = $this->getUrl(); - if (($index = strpos($url, '?')) === false) { + if (false === ($index = strpos($url, '?'))) { return []; - } else { - parse_str(substr($url, $index + 1), $queryParams); - return $queryParams; } + parse_str(substr($url, $index + 1), $queryParams); + + return $queryParams; } + protected $absoluteUrl; + /** * Sets the absolute url. - * - * @param string $url - * @return void */ - function setAbsoluteUrl($url) { - + public function setAbsoluteUrl(string $url) + { $this->absoluteUrl = $url; - } /** * Returns the absolute url. - * - * @return string */ - function getAbsoluteUrl() { + public function getAbsoluteUrl(): string + { + if (!$this->absoluteUrl) { + // Guessing we're a http endpoint. + $this->absoluteUrl = 'http://'. + ($this->getHeader('Host') ?? 'localhost'). + $this->getUrl(); + } return $this->absoluteUrl; - } /** - * Base url + * Base url. * * @var string */ @@ -150,25 +131,18 @@ class Request extends Message implements RequestInterface { * Sets a base url. * * This url is used for relative path calculations. - * - * @param string $url - * @return void */ - function setBaseUrl($url) { - + public function setBaseUrl(string $url) + { $this->baseUrl = $url; - } /** * Returns the current base url. - * - * @return string */ - function getBaseUrl() { - + public function getBaseUrl(): string + { return $this->baseUrl; - } /** @@ -185,33 +159,29 @@ class Request extends Message implements RequestInterface { * ISO-8859-1, it will convert it to UTF-8. * * If the path is outside of the base url, a LogicException will be thrown. - * - * @return string */ - function getPath() { - + public function getPath(): string + { // Removing duplicated slashes. $uri = str_replace('//', '/', $this->getUrl()); $uri = Uri\normalize($uri); $baseUri = Uri\normalize($this->getBaseUrl()); - if (strpos($uri, $baseUri) === 0) { - + if (0 === strpos($uri, $baseUri)) { // We're not interested in the query part (everything after the ?). list($uri) = explode('?', $uri); - return trim(URLUtil::decodePath(substr($uri, strlen($baseUri))), '/'); + return trim(decodePath(substr($uri, strlen($baseUri))), '/'); + } + + if ($uri.'/' === $baseUri) { + return ''; } // A special case, if the baseUri was accessed without a trailing // slash, we'll accept it as well. - elseif ($uri . '/' === $baseUri) { - return ''; - - } - - throw new \LogicException('Requested uri (' . $this->getUrl() . ') is out of base uri (' . $this->getBaseUrl() . ')'); + throw new \LogicException('Requested uri ('.$this->getUrl().') is out of base uri ('.$this->getBaseUrl().')'); } /** @@ -228,27 +198,20 @@ class Request extends Message implements RequestInterface { * * This would not have been needed, if POST data was accessible as * php://input, but unfortunately we need to special case it. - * - * @param array $postData - * @return void */ - function setPostData(array $postData) { - + public function setPostData(array $postData) + { $this->postData = $postData; - } /** * Returns the POST data. * * This is equivalent to PHP's $_POST superglobal. - * - * @return array */ - function getPostData() { - + public function getPostData(): array + { return $this->postData; - } /** @@ -263,54 +226,42 @@ class Request extends Message implements RequestInterface { * * If the value does not exist in the array, null is returned. * - * @param string $valueName * @return string|null */ - function getRawServerValue($valueName) { - - if (isset($this->rawServerData[$valueName])) { - return $this->rawServerData[$valueName]; - } - + public function getRawServerValue(string $valueName) + { + return $this->rawServerData[$valueName] ?? null; } /** * Sets the _SERVER array. - * - * @param array $data - * @return void */ - function setRawServerData(array $data) { - + public function setRawServerData(array $data) + { $this->rawServerData = $data; - } /** * Serializes the request object as a string. * * This is useful for debugging purposes. - * - * @return string */ - function __toString() { - - $out = $this->getMethod() . ' ' . $this->getUrl() . ' HTTP/' . $this->getHTTPVersion() . "\r\n"; + public function __toString(): string + { + $out = $this->getMethod().' '.$this->getUrl().' HTTP/'.$this->getHttpVersion()."\r\n"; foreach ($this->getHeaders() as $key => $value) { foreach ($value as $v) { - if ($key === 'Authorization') { + if ('Authorization' === $key) { list($v) = explode(' ', $v, 2); $v .= ' REDACTED'; } - $out .= $key . ": " . $v . "\r\n"; + $out .= $key.': '.$v."\r\n"; } } $out .= "\r\n"; $out .= $this->getBodyAsString(); return $out; - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/RequestDecorator.php b/htdocs/includes/sabre/sabre/http/lib/RequestDecorator.php index 7ee3f6fc85c..0ad24925f4e 100644 --- a/htdocs/includes/sabre/sabre/http/lib/RequestDecorator.php +++ b/htdocs/includes/sabre/sabre/http/lib/RequestDecorator.php @@ -1,9 +1,11 @@ inner = $inner; - } /** - * Returns the current HTTP method - * - * @return string + * Returns the current HTTP method. */ - function getMethod() { - + public function getMethod(): string + { return $this->inner->getMethod(); - } /** - * Sets the HTTP method - * - * @param string $method - * @return void + * Sets the HTTP method. */ - function setMethod($method) { - + public function setMethod(string $method) + { $this->inner->setMethod($method); - } /** * Returns the request url. - * - * @return string */ - function getUrl() { - + public function getUrl(): string + { return $this->inner->getUrl(); - } /** * Sets the request url. - * - * @param string $url - * @return void */ - function setUrl($url) { - + public function setUrl(string $url) + { $this->inner->setUrl($url); - } /** * Returns the absolute url. - * - * @return string */ - function getAbsoluteUrl() { - + public function getAbsoluteUrl(): string + { return $this->inner->getAbsoluteUrl(); - } /** * Sets the absolute url. - * - * @param string $url - * @return void */ - function setAbsoluteUrl($url) { - + public function setAbsoluteUrl(string $url) + { $this->inner->setAbsoluteUrl($url); - } /** * Returns the current base url. - * - * @return string */ - function getBaseUrl() { - + public function getBaseUrl(): string + { return $this->inner->getBaseUrl(); - } /** @@ -113,14 +88,10 @@ class RequestDecorator implements RequestInterface { * This url is used for relative path calculations. * * The base url should default to / - * - * @param string $url - * @return void */ - function setBaseUrl($url) { - + public function setBaseUrl(string $url) + { $this->inner->setBaseUrl($url); - } /** @@ -137,39 +108,30 @@ class RequestDecorator implements RequestInterface { * ISO-8859-1, it will convert it to UTF-8. * * If the path is outside of the base url, a LogicException will be thrown. - * - * @return string */ - function getPath() { - + public function getPath(): string + { return $this->inner->getPath(); - } /** * Returns the list of query parameters. * * This is equivalent to PHP's $_GET superglobal. - * - * @return array */ - function getQueryParameters() { - + public function getQueryParameters(): array + { return $this->inner->getQueryParameters(); - } /** * Returns the POST data. * * This is equivalent to PHP's $_POST superglobal. - * - * @return array */ - function getPostData() { - + public function getPostData(): array + { return $this->inner->getPostData(); - } /** @@ -179,53 +141,39 @@ class RequestDecorator implements RequestInterface { * * This would not have been needed, if POST data was accessible as * php://input, but unfortunately we need to special case it. - * - * @param array $postData - * @return void */ - function setPostData(array $postData) { - + public function setPostData(array $postData) + { $this->inner->setPostData($postData); - } - /** * Returns an item from the _SERVER array. * * If the value does not exist in the array, null is returned. * - * @param string $valueName * @return string|null */ - function getRawServerValue($valueName) { - + public function getRawServerValue(string $valueName) + { return $this->inner->getRawServerValue($valueName); - } /** * Sets the _SERVER array. - * - * @param array $data - * @return void */ - function setRawServerData(array $data) { - + public function setRawServerData(array $data) + { $this->inner->setRawServerData($data); - } /** * Serializes the request object as a string. * * This is useful for debugging purposes. - * - * @return string */ - function __toString() { - + public function __toString(): string + { return $this->inner->__toString(); - } } diff --git a/htdocs/includes/sabre/sabre/http/lib/RequestInterface.php b/htdocs/includes/sabre/sabre/http/lib/RequestInterface.php index 63d9cbb51a0..83fa85bdc39 100644 --- a/htdocs/includes/sabre/sabre/http/lib/RequestInterface.php +++ b/htdocs/includes/sabre/sabre/http/lib/RequestInterface.php @@ -1,5 +1,7 @@ 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', @@ -81,57 +83,55 @@ class Response extends Message implements ResponseInterface { ]; /** - * HTTP status code + * HTTP status code. * * @var int */ protected $status; /** - * HTTP status text + * HTTP status text. * * @var string */ protected $statusText; /** - * Creates the response object + * Creates the response object. * * @param string|int $status - * @param array $headers - * @param resource $body + * @param array $headers + * @param resource $body */ - function __construct($status = null, array $headers = null, $body = null) { - - if (!is_null($status)) $this->setStatus($status); - if (!is_null($headers)) $this->setHeaders($headers); - if (!is_null($body)) $this->setBody($body); - + public function __construct($status = 500, array $headers = null, $body = null) + { + if (null !== $status) { + $this->setStatus($status); + } + if (null !== $headers) { + $this->setHeaders($headers); + } + if (null !== $body) { + $this->setBody($body); + } } - /** * Returns the current HTTP status code. - * - * @return int */ - function getStatus() { - + public function getStatus(): int + { return $this->status; - } /** * Returns the human-readable status string. * * In the case of a 200, this may for example be 'OK'. - * - * @return string */ - function getStatusText() { - + public function getStatusText(): string + { return $this->statusText; - } /** @@ -144,21 +144,20 @@ class Response extends Message implements ResponseInterface { * added. * * @param string|int $status + * * @throws \InvalidArgumentException - * @return void */ - function setStatus($status) { - + public function setStatus($status) + { if (ctype_digit($status) || is_int($status)) { - $statusCode = $status; - $statusText = isset(self::$statusCodes[$status]) ? self::$statusCodes[$status] : 'Unknown'; - + $statusText = self::$statusCodes[$status] ?? 'Unknown'; } else { list( $statusCode, $statusText ) = explode(' ', $status, 2); + $statusCode = (int) $statusCode; } if ($statusCode < 100 || $statusCode > 999) { throw new \InvalidArgumentException('The HTTP status code must be exactly 3 digits'); @@ -166,28 +165,24 @@ class Response extends Message implements ResponseInterface { $this->status = $statusCode; $this->statusText = $statusText; - } /** * Serializes the response object as a string. * * This is useful for debugging purposes. - * - * @return string */ - function __toString() { - - $str = 'HTTP/' . $this->httpVersion . ' ' . $this->getStatus() . ' ' . $this->getStatusText() . "\r\n"; + public function __toString(): string + { + $str = 'HTTP/'.$this->httpVersion.' '.$this->getStatus().' '.$this->getStatusText()."\r\n"; foreach ($this->getHeaders() as $key => $value) { foreach ($value as $v) { - $str .= $key . ": " . $v . "\r\n"; + $str .= $key.': '.$v."\r\n"; } } $str .= "\r\n"; $str .= $this->getBodyAsString(); + return $str; - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/ResponseDecorator.php b/htdocs/includes/sabre/sabre/http/lib/ResponseDecorator.php index db3a6750718..289dba5cc59 100644 --- a/htdocs/includes/sabre/sabre/http/lib/ResponseDecorator.php +++ b/htdocs/includes/sabre/sabre/http/lib/ResponseDecorator.php @@ -1,9 +1,11 @@ inner = $inner; - } /** * Returns the current HTTP status code. - * - * @return int */ - function getStatus() { - + public function getStatus(): int + { return $this->inner->getStatus(); - } - /** * Returns the human-readable status string. * * In the case of a 200, this may for example be 'OK'. - * - * @return string */ - function getStatusText() { - + public function getStatusText(): string + { return $this->inner->getStatusText(); - } + /** * Sets the HTTP status code. * @@ -61,12 +54,10 @@ class ResponseDecorator implements ResponseInterface { * added. * * @param string|int $status - * @return void */ - function setStatus($status) { - + public function setStatus($status) + { $this->inner->setStatus($status); - } /** @@ -76,9 +67,8 @@ class ResponseDecorator implements ResponseInterface { * * @return string */ - function __toString() { - + public function __toString(): string + { return $this->inner->__toString(); - } } diff --git a/htdocs/includes/sabre/sabre/http/lib/ResponseInterface.php b/htdocs/includes/sabre/sabre/http/lib/ResponseInterface.php index 411cdc06cbb..9bd93f179ad 100644 --- a/htdocs/includes/sabre/sabre/http/lib/ResponseInterface.php +++ b/htdocs/includes/sabre/sabre/http/lib/ResponseInterface.php @@ -1,5 +1,7 @@ setBody(fopen('php://input', 'r')); $r->setPostData($_POST); - return $r; + return $r; } /** * Sends the HTTP response back to a HTTP client. * * This calls php's header() function and streams the body to php://output. - * - * @param ResponseInterface $response - * @return void */ - static function sendResponse(ResponseInterface $response) { - - header('HTTP/' . $response->getHttpVersion() . ' ' . $response->getStatus() . ' ' . $response->getStatusText()); + public static function sendResponse(ResponseInterface $response) + { + header('HTTP/'.$response->getHttpVersion().' '.$response->getStatus().' '.$response->getStatusText()); foreach ($response->getHeaders() as $key => $value) { - foreach ($value as $k => $v) { - if ($k === 0) { - header($key . ': ' . $v); + if (0 === $k) { + header($key.': '.$v); } else { - header($key . ': ' . $v, false); + header($key.': '.$v, false); } } - } $body = $response->getBody(); - if (is_null($body)) return; + if (null === $body) { + return; + } + + if (is_callable($body)) { + $body(); + + return; + } $contentLength = $response->getHeader('Content-Length'); - if ($contentLength !== null) { + if (null !== $contentLength) { $output = fopen('php://output', 'wb'); - if (is_resource($body) && get_resource_type($body) == 'stream') { - stream_copy_to_stream($body, $output, $contentLength); + if (is_resource($body) && 'stream' == get_resource_type($body)) { + if (PHP_INT_SIZE > 4) { + // use the dedicated function on 64 Bit systems + // a workaround to make PHP more possible to use mmap based copy, see https://github.com/sabre-io/http/pull/119 + $left = (int) $contentLength; + // copy with 4MiB chunks + $chunk_size = 4 * 1024 * 1024; + stream_set_chunk_size($output, $chunk_size); + // If this is a partial response, flush the beginning bytes until the first position that is a multiple of the page size. + $contentRange = $response->getHeader('Content-Range'); + // Matching "Content-Range: bytes 1234-5678/7890" + if (null !== $contentRange && preg_match('/^bytes\s([0-9]+)-([0-9]+)\//i', $contentRange, $matches)) { + // 4kB should be the default page size on most architectures + $pageSize = 4096; + $offset = (int) $matches[1]; + $delta = ($offset % $pageSize) > 0 ? ($pageSize - $offset % $pageSize) : 0; + if ($delta > 0) { + $left -= stream_copy_to_stream($body, $output, min($delta, $left)); + } + } + while ($left > 0) { + $copied = stream_copy_to_stream($body, $output, min($left, $chunk_size)); + // stream_copy_to_stream($src, $dest, $maxLength) must return the number of bytes copied or false in case of failure + // But when the $maxLength is greater than the total number of bytes remaining in the stream, + // It returns the negative number of bytes copied + // So break the loop in such cases. + if ($copied <= 0) { + break; + } + $left -= $copied; + } + } else { + // workaround for 32 Bit systems to avoid stream_copy_to_stream + while (!feof($body)) { + fwrite($output, fread($body, 8192)); + } + } } else { - fwrite($output, $body, $contentLength); + fwrite($output, $body, (int) $contentLength); } } else { file_put_contents('php://output', $body); @@ -86,18 +135,16 @@ class Sapi { if (is_resource($body)) { fclose($body); } - } /** * This static method will create a new Request object, based on a PHP * $_SERVER array. * - * @param array $serverArray - * @return Request + * REQUEST_URI and REQUEST_METHOD are required. */ - static function createFromServerArray(array $serverArray) { - + public static function createFromServerArray(array $serverArray): Request + { $headers = []; $method = null; $url = null; @@ -107,61 +154,61 @@ class Sapi { $hostName = 'localhost'; foreach ($serverArray as $key => $value) { - switch ($key) { - - case 'SERVER_PROTOCOL' : - if ($value === 'HTTP/1.0') { + case 'SERVER_PROTOCOL': + if ('HTTP/1.0' === $value) { $httpVersion = '1.0'; + } elseif ('HTTP/2.0' === $value) { + $httpVersion = '2.0'; } break; - case 'REQUEST_METHOD' : + case 'REQUEST_METHOD': $method = $value; break; - case 'REQUEST_URI' : + case 'REQUEST_URI': $url = $value; break; // These sometimes show up without a HTTP_ prefix - case 'CONTENT_TYPE' : + case 'CONTENT_TYPE': $headers['Content-Type'] = $value; break; - case 'CONTENT_LENGTH' : + case 'CONTENT_LENGTH': $headers['Content-Length'] = $value; break; // mod_php on apache will put credentials in these variables. // (fast)cgi does not usually do this, however. - case 'PHP_AUTH_USER' : + case 'PHP_AUTH_USER': if (isset($serverArray['PHP_AUTH_PW'])) { - $headers['Authorization'] = 'Basic ' . base64_encode($value . ':' . $serverArray['PHP_AUTH_PW']); + $headers['Authorization'] = 'Basic '.base64_encode($value.':'.$serverArray['PHP_AUTH_PW']); } break; // Similarly, mod_php may also screw around with digest auth. - case 'PHP_AUTH_DIGEST' : - $headers['Authorization'] = 'Digest ' . $value; + case 'PHP_AUTH_DIGEST': + $headers['Authorization'] = 'Digest '.$value; break; // Apache may prefix the HTTP_AUTHORIZATION header with // REDIRECT_, if mod_rewrite was used. - case 'REDIRECT_HTTP_AUTHORIZATION' : + case 'REDIRECT_HTTP_AUTHORIZATION': $headers['Authorization'] = $value; break; - case 'HTTP_HOST' : + case 'HTTP_HOST': $hostName = $value; $headers['Host'] = $value; break; - case 'HTTPS' : - if (!empty($value) && $value !== 'off') { + case 'HTTPS': + if (!empty($value) && 'off' !== $value) { $protocol = 'https'; } break; - default : - if (substr($key, 0, 5) === 'HTTP_') { + default: + if ('HTTP_' === substr($key, 0, 5)) { // It's a HTTP header // Normalizing it to be prettier @@ -174,21 +221,23 @@ class Sapi { // Turning spaces into dashes. $header = str_replace(' ', '-', $header); $headers[$header] = $value; - } break; - - } - } + if (null === $url) { + throw new InvalidArgumentException('The _SERVER array must have a REQUEST_URI key'); + } + + if (null === $method) { + throw new InvalidArgumentException('The _SERVER array must have a REQUEST_METHOD key'); + } $r = new Request($method, $url, $headers); $r->setHttpVersion($httpVersion); $r->setRawServerData($serverArray); - $r->setAbsoluteUrl($protocol . '://' . $hostName . $url); + $r->setAbsoluteUrl($protocol.'://'.$hostName.$url); + return $r; - } - } diff --git a/htdocs/includes/sabre/sabre/http/lib/URLUtil.php b/htdocs/includes/sabre/sabre/http/lib/URLUtil.php deleted file mode 100644 index 85c0e11504a..00000000000 --- a/htdocs/includes/sabre/sabre/http/lib/URLUtil.php +++ /dev/null @@ -1,103 +0,0 @@ -setTimezone(new \DateTimeZone('GMT')); - return $dateTime->format('D, d M Y H:i:s \G\M\T'); + return $dateTime->format('D, d M Y H:i:s \G\M\T'); } /** @@ -101,11 +107,12 @@ function toDate(DateTime $dateTime) { * implying that no accept header was sent. * * @param string|null $acceptHeaderValue - * @param array $availableOptions + * @param array $availableOptions + * * @return string|null */ -function negotiateContentType($acceptHeaderValue, array $availableOptions) { - +function negotiateContentType($acceptHeaderValue, array $availableOptions) +{ if (!$acceptHeaderValue) { // Grabbing the first in the list. return reset($availableOptions); @@ -130,9 +137,10 @@ function negotiateContentType($acceptHeaderValue, array $availableOptions) { $lastChoice = null; foreach ($proposals as $proposal) { - // Ignoring broken values. - if (is_null($proposal)) continue; + if (null === $proposal) { + continue; + } // If the quality is lower we don't have to bother comparing. if ($proposal['quality'] < $lastQuality) { @@ -140,12 +148,11 @@ function negotiateContentType($acceptHeaderValue, array $availableOptions) { } foreach ($options as $optionIndex => $option) { - - if ($proposal['type'] !== '*' && $proposal['type'] !== $option['type']) { + if ('*' !== $proposal['type'] && $proposal['type'] !== $option['type']) { // no match on type. continue; } - if ($proposal['subType'] !== '*' && $proposal['subType'] !== $option['subType']) { + if ('*' !== $proposal['subType'] && $proposal['subType'] !== $option['subType']) { // no match on subtype. continue; } @@ -165,31 +172,25 @@ function negotiateContentType($acceptHeaderValue, array $availableOptions) { // subtype. We need to calculate a score for how specific the // match was. $specificity = - ($proposal['type'] !== '*' ? 20 : 0) + - ($proposal['subType'] !== '*' ? 10 : 0) + + ('*' !== $proposal['type'] ? 20 : 0) + + ('*' !== $proposal['subType'] ? 10 : 0) + count($option['parameters']); - // Does this entry win? if ( ($proposal['quality'] > $lastQuality) || ($proposal['quality'] === $lastQuality && $specificity > $lastSpecificity) || ($proposal['quality'] === $lastQuality && $specificity === $lastSpecificity && $optionIndex < $lastOptionIndex) ) { - $lastQuality = $proposal['quality']; $lastSpecificity = $specificity; $lastOptionIndex = $optionIndex; $lastChoice = $availableOptions[$optionIndex]; - } - } - } return $lastChoice; - } /** @@ -217,10 +218,11 @@ function negotiateContentType($acceptHeaderValue, array $availableOptions) { * uses them. * * @param string|string[] $input + * * @return array */ -function parsePrefer($input) { - +function parsePrefer($input): array +{ $token = '[!#$%&\'*+\-.^_`~A-Za-z0-9]+'; // Work in progress @@ -241,7 +243,6 @@ REGEX; $output = []; foreach (getHeaderValues($input) as $value) { - if (!preg_match($regex, $value, $matches)) { // Ignore continue; @@ -249,22 +250,22 @@ REGEX; // Mapping old values to their new counterparts switch ($matches['name']) { - case 'return-asynch' : + case 'return-asynch': $output['respond-async'] = true; break; - case 'return-representation' : + case 'return-representation': $output['return'] = 'representation'; break; - case 'return-minimal' : + case 'return-minimal': $output['return'] = 'minimal'; break; - case 'strict' : + case 'strict': $output['handling'] = 'strict'; break; - case 'lenient' : + case 'lenient': $output['handling'] = 'lenient'; break; - default : + default: if (isset($matches['value'])) { $value = trim($matches['value'], '"'); } else { @@ -273,11 +274,9 @@ REGEX; $output[strtolower($matches['name'])] = empty($value) ? true : $value; break; } - } return $output; - } /** @@ -296,25 +295,26 @@ REGEX; * * @param string|string[] $values * @param string|string[] $values2 - * @return string[] */ -function getHeaderValues($values, $values2 = null) { - - $values = (array)$values; +function getHeaderValues($values, $values2 = null): array +{ + $values = (array) $values; if ($values2) { - $values = array_merge($values, (array)$values2); + $values = array_merge($values, (array) $values2); } + + $result = array(); foreach ($values as $l1) { foreach (explode(',', $l1) as $l2) { $result[] = trim($l2); } } - return $result; + return $result; } /** - * Parses a mime-type and splits it into: + * Parses a mime-type and splits it into:. * * 1. type * 2. subtype @@ -322,10 +322,11 @@ function getHeaderValues($values, $values2 = null) { * 4. parameters * * @param string $str + * * @return array */ -function parseMimeType($str) { - +function parseMimeType(string $str): array +{ $parameters = []; // If no q= parameter appears, then quality = 1. $quality = 1; @@ -333,17 +334,22 @@ function parseMimeType($str) { $parts = explode(';', $str); // The first part is the mime-type. - $mimeType = array_shift($parts); + $mimeType = trim(array_shift($parts)); - $mimeType = explode('/', trim($mimeType)); - if (count($mimeType) !== 2) { + if ('*' === $mimeType) { + $mimeType = '*/*'; + } + + $mimeType = explode('/', $mimeType); + if (2 !== count($mimeType)) { // Illegal value - return null; + var_dump($mimeType); + die(); + throw new InvalidArgumentException('Not a valid mime-type: '.$str); } list($type, $subType) = $mimeType; foreach ($parts as $part) { - $part = trim($part); if (strpos($part, '=')) { list($partName, $partValue) = @@ -357,89 +363,66 @@ function parseMimeType($str) { // the parameter list. Anything after the q= counts as an // 'accept extension' and could introduce new semantics in // content-negotation. - if ($partName !== 'q') { + if ('q' !== $partName) { $parameters[$partName] = $part; } else { - $quality = (float)$partValue; + $quality = (float) $partValue; break; // Stop parsing parts } - } return [ - 'type' => $type, - 'subType' => $subType, - 'quality' => $quality, + 'type' => $type, + 'subType' => $subType, + 'quality' => $quality, 'parameters' => $parameters, ]; - } /** * Encodes the path of a url. * * slashes (/) are treated as path-separators. - * - * @param string $path - * @return string */ -function encodePath($path) { - - return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/:@])/', function($match) { - - return '%' . sprintf('%02x', ord($match[0])); - +function encodePath(string $path): string +{ + return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/:@])/', function ($match) { + return '%'.sprintf('%02x', ord($match[0])); }, $path); - } /** - * Encodes a 1 segment of a path + * Encodes a 1 segment of a path. * * Slashes are considered part of the name, and are encoded as %2f - * - * @param string $pathSegment - * @return string */ -function encodePathSegment($pathSegment) { - - return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\):@])/', function($match) { - - return '%' . sprintf('%02x', ord($match[0])); - +function encodePathSegment(string $pathSegment): string +{ + return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\):@])/', function ($match) { + return '%'.sprintf('%02x', ord($match[0])); }, $pathSegment); } /** - * Decodes a url-encoded path - * - * @param string $path - * @return string + * Decodes a url-encoded path. */ -function decodePath($path) { - +function decodePath(string $path): string +{ return decodePathSegment($path); - } /** - * Decodes a url-encoded path segment - * - * @param string $path - * @return string + * Decodes a url-encoded path segment. */ -function decodePathSegment($path) { - +function decodePathSegment(string $path): string +{ $path = rawurldecode($path); $encoding = mb_detect_encoding($path, ['UTF-8', 'ISO-8859-1']); switch ($encoding) { - - case 'ISO-8859-1' : + case 'ISO-8859-1': $path = utf8_encode($path); - } return $path; - } diff --git a/htdocs/includes/sabre/sabre/http/phpstan.neon b/htdocs/includes/sabre/sabre/http/phpstan.neon new file mode 100644 index 00000000000..241663a7271 --- /dev/null +++ b/htdocs/includes/sabre/sabre/http/phpstan.neon @@ -0,0 +1,3 @@ +parameters: + level: 1 + bootstrap: %currentWorkingDirectory%/vendor/autoload.php diff --git a/htdocs/includes/sabre/sabre/uri/.gitignore b/htdocs/includes/sabre/sabre/uri/.gitignore deleted file mode 100644 index 19d1affd4a7..00000000000 --- a/htdocs/includes/sabre/sabre/uri/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Composer -vendor/ -composer.lock - -# Tests -tests/cov/ - -# Composer binaries -bin/phpunit -bin/phpcs - -# Vim -.*.swp diff --git a/htdocs/includes/sabre/sabre/uri/.travis.yml b/htdocs/includes/sabre/sabre/uri/.travis.yml deleted file mode 100644 index 75c8270df21..00000000000 --- a/htdocs/includes/sabre/sabre/uri/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: php -php: - - 5.4 - - 5.5 - - 5.6 - - 7 - - 7.1 - -script: - - ./bin/phpunit --configuration tests/phpunit.xml.dist - - ./bin/sabre-cs-fixer fix lib/ --dry-run --diff - -before_script: composer install --dev - diff --git a/htdocs/includes/sabre/sabre/uri/CHANGELOG.md b/htdocs/includes/sabre/sabre/uri/CHANGELOG.md deleted file mode 100644 index a30e45139ae..00000000000 --- a/htdocs/includes/sabre/sabre/uri/CHANGELOG.md +++ /dev/null @@ -1,51 +0,0 @@ -ChangeLog -========= - -1.2.0 (2016-12-06) ------------------- - -* Now throwing `InvalidUriException` if a uri passed to the `parse` function - is invalid or could not be parsed. -* #11: Fix support for URIs that start with a triple slash. PHP's `parse_uri()` - doesn't support them, so we now have a pure-php fallback in case it fails. -* #9: Fix support for relative URI's that have a non-uri encoded colon `:` in - them. - - -1.1.1 (2016-10-27) ------------------- - -* #10: Correctly support file:// URIs in the build() method. (@yuloh) - - -1.1.0 (2016-03-07) ------------------- - -* #6: PHP's `parse_url()` corrupts strings if they contain certain - non ascii-characters such as Chinese or Hebrew. sabre/uri's `parse()` - function now percent-encodes these characters beforehand. - - -1.0.1 (2015-04-28) ------------------- - -* #4: Using php-cs-fixer to automatically enforce conding standards. -* #5: Resolving to and building `mailto:` urls were not correctly handled. - - -1.0.0 (2015-01-27) ------------------- - -* Added a `normalize` function. -* Added a `buildUri` function. -* Fixed a bug in the `resolve` when only a new fragment is specified. - -San José, CalConnect XXXII release! - -0.0.1 (2014-11-17) ------------------- - -* First version! -* Source was lifted from sabre/http package. -* Provides a `resolve` and a `split` function. -* Requires PHP 5.4.8 and up. diff --git a/htdocs/includes/sabre/sabre/uri/LICENSE b/htdocs/includes/sabre/sabre/uri/LICENSE index 9a3a9194600..ae2c992901c 100644 --- a/htdocs/includes/sabre/sabre/uri/LICENSE +++ b/htdocs/includes/sabre/sabre/uri/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2014-2016 fruux GmbH (https://fruux.com/) +Copyright (C) 2014-2019 fruux GmbH (https://fruux.com/) All rights reserved. diff --git a/htdocs/includes/sabre/sabre/uri/README.md b/htdocs/includes/sabre/sabre/uri/README.md deleted file mode 100644 index 76f55d8e454..00000000000 --- a/htdocs/includes/sabre/sabre/uri/README.md +++ /dev/null @@ -1,55 +0,0 @@ -sabre/uri -========= - -sabre/uri is a lightweight library that provides several functions for working -with URIs, staying true to the rules of [RFC3986][2]. - -Partially inspired by [Node.js URL library][3], and created to solve real -problems in PHP applications. 100% unitested and many tests are based on -examples from RFC3986. - -The library provides the following functions: - -1. `resolve` to resolve relative urls. -2. `normalize` to aid in comparing urls. -3. `parse`, which works like PHP's [parse_url][6]. -4. `build` to do the exact opposite of `parse`. -5. `split` to easily get the 'dirname' and 'basename' of a URL without all the - problems those two functions have. - - -Further reading ---------------- - -* [Installation][7] -* [Usage][8] - - -Build status ------------- - -| branch | status | -| ------ | ------ | -| master | [![Build Status](https://travis-ci.org/fruux/sabre-uri.svg?branch=master)](https://travis-ci.org/fruux/sabre-uri) | - - -Questions? ----------- - -Head over to the [sabre/dav mailinglist][4], or you can also just open a ticket -on [GitHub][5]. - - -Made at fruux -------------- - -This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. - -[1]: http://sabre.io/uri/ -[2]: https://tools.ietf.org/html/rfc3986/ -[3]: http://nodejs.org/api/url.html -[4]: http://groups.google.com/group/sabredav-discuss -[5]: https://github.com/fruux/sabre-uri/issues/ -[6]: http://php.net/manual/en/function.parse-url.php -[7]: http://sabre.io/uri/install/ -[8]: http://sabre.io/uri/usage/ diff --git a/htdocs/includes/sabre/sabre/uri/composer.json b/htdocs/includes/sabre/sabre/uri/composer.json index efa5a693004..859c3c03e46 100644 --- a/htdocs/includes/sabre/sabre/uri/composer.json +++ b/htdocs/includes/sabre/sabre/uri/composer.json @@ -9,7 +9,7 @@ "homepage": "http://sabre.io/uri/", "license": "BSD-3-Clause", "require": { - "php": ">=5.4.7" + "php": "^7.1 || ^8.0" }, "authors": [ { @@ -31,11 +31,30 @@ "Sabre\\Uri\\" : "lib/" } }, - "require-dev": { - "sabre/cs": "~1.0.0", - "phpunit/phpunit" : "*" + "autoload-dev": { + "psr-4": { + "Sabre\\Uri\\": "tests/Uri" + } }, - "config" : { - "bin-dir" : "bin/" + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.19.3", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit" : "^7.5 || ^8.5 || ^9.0" + }, + "scripts": { + "phpstan": [ + "phpstan analyse lib tests" + ], + "cs-fixer": [ + "php-cs-fixer fix" + ], + "phpunit": [ + "phpunit --configuration tests/phpunit.xml" + ], + "test": [ + "composer phpstan", + "composer cs-fixer", + "composer phpunit" + ] } } diff --git a/htdocs/includes/sabre/sabre/uri/lib/InvalidUriException.php b/htdocs/includes/sabre/sabre/uri/lib/InvalidUriException.php index 0385fd4628d..7f37ca54ecd 100644 --- a/htdocs/includes/sabre/sabre/uri/lib/InvalidUriException.php +++ b/htdocs/includes/sabre/sabre/uri/lib/InvalidUriException.php @@ -1,9 +1,11 @@ 0) { // If the path starts with a slash - if ($delta['path'][0] === '/') { + if ('/' === $delta['path'][0]) { $path = $delta['path']; } else { // Removing last component from base path. $path = $base['path']; - if (strpos($path, '/') !== false) { - $path = substr($path, 0, strrpos($path, '/')); + $length = strrpos((string) $path, '/'); + if (false !== $length) { + $path = substr($path, 0, $length); } - $path .= '/' . $delta['path']; + $path .= '/'.$delta['path']; } } else { $path = $base['path'] ?: '/'; @@ -68,15 +68,14 @@ function resolve($basePath, $newPath) { $pathParts = explode('/', $path); $newPathParts = []; foreach ($pathParts as $pathPart) { - switch ($pathPart) { //case '' : - case '.' : + case '.': break; - case '..' : + case '..': array_pop($newPathParts); break; - default : + default: $newPathParts[] = $pathPart; break; } @@ -85,7 +84,7 @@ function resolve($basePath, $newPath) { $path = implode('/', $newPathParts); // If the source url ended with a /, we want to preserve that. - $newParts['path'] = $path; + $newParts['path'] = 0 === strpos($path, '/') ? $path : '/'.$path; if ($delta['query']) { $newParts['query'] = $delta['query']; } elseif (!empty($base['query']) && empty($delta['host']) && empty($delta['path'])) { @@ -95,8 +94,8 @@ function resolve($basePath, $newPath) { if ($delta['fragment']) { $newParts['fragment'] = $delta['fragment']; } - return build($newParts); + return build($newParts); } /** @@ -108,11 +107,10 @@ function resolve($basePath, $newPath) { * * It will also change a %3a into a %3A. * - * @param string $uri - * @return string + * @throws InvalidUriException */ -function normalize($uri) { - +function normalize(string $uri): string +{ $parts = parse($uri); if (!empty($parts['path'])) { @@ -123,23 +121,23 @@ function normalize($uri) { case '.': // skip break; - case '..' : + case '..': // One level up in the hierarchy array_pop($newPathParts); break; - default : + default: // Ensuring that everything is correctly percent-encoded. $newPathParts[] = rawurlencode(rawurldecode($pathPart)); break; } } - $parts['path'] = '/' . implode('/', $newPathParts); + $parts['path'] = '/'.implode('/', $newPathParts); } if ($parts['scheme']) { $parts['scheme'] = strtolower($parts['scheme']); $defaultPorts = [ - 'http' => '80', + 'http' => '80', 'https' => '443', ]; @@ -149,8 +147,8 @@ function normalize($uri) { } // A few HTTP specific rules. switch ($parts['scheme']) { - case 'http' : - case 'https' : + case 'http': + case 'https': if (empty($parts['path'])) { // An empty path is equivalent to / in http. $parts['path'] = '/'; @@ -159,10 +157,11 @@ function normalize($uri) { } } - if ($parts['host']) $parts['host'] = strtolower($parts['host']); + if ($parts['host']) { + $parts['host'] = strtolower($parts['host']); + } return build($parts); - } /** @@ -175,11 +174,12 @@ function normalize($uri) { * Unlike PHP's parse_url, it will also convert any non-ascii characters to * percent-encoded strings. PHP's parse_url corrupts these characters on OS X. * - * @param string $uri - * @return array + * @return array + * + * @throws InvalidUriException */ -function parse($uri) { - +function parse(string $uri): array +{ // Normally a URI must be ASCII, however. However, often it's not and // parse_url might corrupt these strings. // @@ -187,7 +187,7 @@ function parse($uri) { // uriencode them first. $uri = preg_replace_callback( '/[^[:ascii:]]/u', - function($matches) { + function ($matches) { return rawurlencode($matches[0]); }, $uri @@ -196,66 +196,74 @@ function parse($uri) { $result = parse_url($uri); if (!$result) { $result = _parse_fallback($uri); + } else { + // Add empty host and trailing slash to Windows file paths + // file:///C:/path or file:///C:\path + // Note: the regex fragment [a-zA-Z]:[\/\\\\].* end up being + // [a-zA-Z]:[\/\\].* + // The 4 backslash in a row are the way to get 2 backslash into the actual string + // that is used as the regex. The 2 backslash are then the way to get 1 backslash + // character into the character set "a forward slash or a backslash" + if (isset($result['scheme']) && 'file' === $result['scheme'] && isset($result['path']) && + preg_match('/^(? [a-zA-Z]:([\/\\\\].*)?)$/x', $result['path'])) { + $result['path'] = '/'.$result['path']; + $result['host'] = ''; + } } return $result + [ - 'scheme' => null, - 'host' => null, - 'path' => null, - 'port' => null, - 'user' => null, - 'query' => null, + 'scheme' => null, + 'host' => null, + 'path' => null, + 'port' => null, + 'user' => null, + 'query' => null, 'fragment' => null, ]; - } /** * This function takes the components returned from PHP's parse_url, and uses * it to generate a new uri. * - * @param array $parts - * @return string + * @param array $parts */ -function build(array $parts) { - +function build(array $parts): string +{ $uri = ''; $authority = ''; if (!empty($parts['host'])) { $authority = $parts['host']; if (!empty($parts['user'])) { - $authority = $parts['user'] . '@' . $authority; + $authority = $parts['user'].'@'.$authority; } if (!empty($parts['port'])) { - $authority = $authority . ':' . $parts['port']; + $authority = $authority.':'.$parts['port']; } } if (!empty($parts['scheme'])) { // If there's a scheme, there's also a host. - $uri = $parts['scheme'] . ':'; - + $uri = $parts['scheme'].':'; } - if ($authority || (!empty($parts['scheme']) && $parts['scheme'] === 'file')) { + if ($authority || (!empty($parts['scheme']) && 'file' === $parts['scheme'])) { // No scheme, but there is a host. - $uri .= '//' . $authority; - + $uri .= '//'.$authority; } if (!empty($parts['path'])) { $uri .= $parts['path']; } if (!empty($parts['query'])) { - $uri .= '?' . $parts['query']; + $uri .= '?'.$parts['query']; } if (!empty($parts['fragment'])) { - $uri .= '#' . $parts['fragment']; + $uri .= '#'.$parts['fragment']; } return $uri; - } /** @@ -266,24 +274,23 @@ function build(array $parts) { * is used) and we need a method that just operates on UTF-8 characters. * * In addition basename and dirname are platform aware, and will treat - * backslash (\) as a directory separator on windows. + * backslash (\) as a directory separator on Windows. * * This method returns the 2 components as an array. * * If there is no dirname, it will return an empty string. Any / appearing at * the end of the string is stripped off. * - * @param string $path - * @return array + * @return array */ -function split($path) { - +function split(string $path): array +{ $matches = []; if (preg_match('/^(?:(?:(.*)(?:\/+))?([^\/]+))(?:\/?)$/u', $path, $matches)) { return [$matches[1], $matches[2]]; } - return [null,null]; + return [null, null]; } /** @@ -296,11 +303,12 @@ function split($path) { * This function is only called if the main parse method fails. It's pretty * crude and probably slow, so the original parse_url is usually preferred. * - * @param string $uri - * @return array + * @return array + * + * @throws InvalidUriException */ -function _parse_fallback($uri) { - +function _parse_fallback(string $uri): array +{ // Normally a URI must be ASCII, however. However, often it's not and // parse_url might corrupt these strings. // @@ -308,63 +316,69 @@ function _parse_fallback($uri) { // uriencode them first. $uri = preg_replace_callback( '/[^[:ascii:]]/u', - function($matches) { + function ($matches) { return rawurlencode($matches[0]); }, $uri ); $result = [ - 'scheme' => null, - 'host' => null, - 'port' => null, - 'user' => null, - 'path' => null, + 'scheme' => null, + 'host' => null, + 'port' => null, + 'user' => null, + 'path' => null, 'fragment' => null, - 'query' => null, + 'query' => null, ]; if (preg_match('% ^([A-Za-z][A-Za-z0-9+-\.]+): %x', $uri, $matches)) { - $result['scheme'] = $matches[1]; // Take what's left. $uri = substr($uri, strlen($result['scheme']) + 1); - } // Taking off a fragment part - if (strpos($uri, '#')) { + if (false !== strpos($uri, '#')) { list($uri, $result['fragment']) = explode('#', $uri, 2); } // Taking off the query part - if (strpos($uri, '?')) { + if (false !== strpos($uri, '?')) { list($uri, $result['query']) = explode('?', $uri, 2); } - if (substr($uri, 0, 3) === '///') { - // The triple slash uris are a bit unusual, but we have special handling - // for them. - $result['path'] = substr($uri, 2); - $result['host'] = ''; - } elseif (substr($uri, 0, 2) === '//') { + if ('///' === substr($uri, 0, 3)) { + // The triple slash uris are a bit unusual, but we have special handling + // for them. + $result['path'] = substr($uri, 2); + $result['host'] = ''; + } elseif ('//' === substr($uri, 0, 2)) { // Uris that have an authority part. - $regex = ' - %^ + $regex = '%^ // (?: (? [^:@]+) (: (? [^@]+)) @)? (? ( [^:/]* | \[ [^\]]+ \] )) (?: : (? [0-9]+))? (? / .*)? - $%x - '; + $%x'; if (!preg_match($regex, $uri, $matches)) { throw new InvalidUriException('Invalid, or could not parse URI'); } - if ($matches['host']) $result['host'] = $matches['host']; - if ($matches['port']) $result['port'] = (int)$matches['port']; - if (isset($matches['path'])) $result['path'] = $matches['path']; - if ($matches['user']) $result['user'] = $matches['user']; - if ($matches['pass']) $result['pass'] = $matches['pass']; + if ($matches['host']) { + $result['host'] = $matches['host']; + } + if (isset($matches['port'])) { + $result['port'] = (int) $matches['port']; + } + if (isset($matches['path'])) { + $result['path'] = $matches['path']; + } + if ($matches['user']) { + $result['user'] = $matches['user']; + } + if ($matches['pass']) { + $result['pass'] = $matches['pass']; + } } else { $result['path'] = $uri; } diff --git a/htdocs/includes/sabre/sabre/xml/.gitignore b/htdocs/includes/sabre/sabre/xml/.gitignore deleted file mode 100644 index accb586c771..00000000000 --- a/htdocs/includes/sabre/sabre/xml/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -vendor -composer.lock -tests/cov -.*.swp - -# Composer binaries -bin/phpunit -bin/php-cs-fixer -bin/sabre-cs-fixer diff --git a/htdocs/includes/sabre/sabre/xml/.travis.yml b/htdocs/includes/sabre/sabre/xml/.travis.yml deleted file mode 100644 index 96396564e76..00000000000 --- a/htdocs/includes/sabre/sabre/xml/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -language: php -php: - - 5.5 - - 5.6 - - 7.0 - - 7.1 - -matrix: - fast_finish: true - -sudo: false - -cache: - directories: - - $HOME/.composer/cache - -before_install: - - phpenv config-rm xdebug.ini; true - -install: - - composer install - -script: - - ./bin/phpunit --configuration tests/phpunit.xml.dist - - ./bin/sabre-cs-fixer fix . --dry-run --diff - diff --git a/htdocs/includes/sabre/sabre/xml/CHANGELOG.md b/htdocs/includes/sabre/sabre/xml/CHANGELOG.md deleted file mode 100644 index 39a39bffe9f..00000000000 --- a/htdocs/includes/sabre/sabre/xml/CHANGELOG.md +++ /dev/null @@ -1,228 +0,0 @@ -ChangeLog -========= - -1.5.0 (2016-10-09) ------------------- - -* Now requires PHP 5.5. -* Using `finally` to always roll back the context stack when serializing. -* #94: Fixed an infinite loop condition when reading some invalid XML - documents. - - -1.4.2 (2016-05-19) ------------------- - -* The `contextStack` in the Reader object is now correctly rolled back in - error conditions (@staabm). -* repeatingElements deserializer now still parses if a bare element name - without clark notation was given. -* `$elementMap` in the Reader now also supports bare element names. -* `Service::expect()` can now also work with bare element names. - - -1.4.1 (2016-03-12) ------------------ - -* Parsing clark-notation is now cached. This can speed up parsing large - documents with lots of repeating elements a fair bit. (@icewind1991). - - -1.4.0 (2016-02-14) ------------------- - -* Any array thrown into the serializer with numeric keys is now simply - traversed and each individual item is serialized. This fixes an issue - related to serializing value objects with array children. -* When serializing value objects, properties that have a null value or an - empty array are now skipped. We believe this to be the saner default, but - does constitute a BC break for those depending on this. -* Serializing array properties in value objects was broken. - - -1.3.0 (2015-12-29) ------------------- - -* The `Service` class adds a new `mapValueObject` method which provides basic - capabilities to map between ValueObjects and XML. -* #61: You can now specify serializers for specific classes, allowing you - separate the object you want to serialize from the serializer. This uses the - `$classMap` property which is defined on both the `Service` and `Writer`. -* It's now possible to pass an array of possible root elements to - `Sabre\Xml\Service::expect()`. -* Moved some parsing logic to `Reader::getDeserializerForElementName()`, - so people with more advanced use-cases can implement their own logic there. -* #63: When serializing elements using arrays, the `value` key in the array is - now optional. -* #62: Added a `keyValue` deserializer function. This can be used instead of - the `Element\KeyValue` class and is a lot more flexible. (@staabm) -* Also added an `enum` deserializer function to replace - `Element\Elements`. -* Using an empty string for a namespace prefix now has the same effect as - `null`. - - -1.2.0 (2015-08-30) ------------------- - -* #53: Added `parseGetElements`, a function like `parseInnerTree`, except - that it always returns an array of elements, or an empty array. - - -1.1.0 (2015-06-29) ------------------- - -* #44, #45: Catching broken and invalid XML better and throwing - `Sabre\Xml\LibXMLException` whenever we encounter errors. (@stefanmajoor, - @DaanBiesterbos) - - -1.0.0 (2015-05-25) ------------------- - -* No functional changes since 0.4.3. Marking it as 1.0.0 as a promise for - API stability. -* Using php-cs-fixer for automated CS enforcement. - - -0.4.3 (2015-04-01) ------------------ - -* Minor tweaks for the public release. - - -0.4.2 (2015-03-20) ------------------- - -* Removed `constants.php` again. They messed with PHPUnit and don't really - provide a great benefit. -* #41: Correctly handle self-closing xml elements. - - -0.4.1 (2015-03-19) ------------------- - -* #40: An element with an empty namespace (xmlns="") is not allowed to have a - prefix. This is now fixed. - - -0.4.0 (2015-03-18) ------------------- - -* Added `Sabre\Xml\Service`. This is intended as a simple way to centrally - configure xml applications and easily parse/write things from there. #35, #38. -* Renamed 'baseUri' to 'contextUri' everywhere. -* #36: Added a few convenience constants to `lib/constants.php`. -* `Sabre\Xml\Util::parseClarkNotation` is now in the `Sabre\Xml\Service` class. - - -0.3.1 (2015-02-08) ------------------- - -* Added `XmlDeserializable` to match `XmlSerializable`. - - -0.3.0 (2015-02-06) ------------------- - -* Added `$elementMap` argument to parseInnerTree, for quickly overriding - parsing rules within an element. - - -0.2.2 (2015-02-05) ------------------- - -* Now depends on sabre/uri 1.0. - - -0.2.1 (2014-12-17) ------------------- - -* LibXMLException now inherits from ParseException, so it's easy for users to - catch any exception thrown by the parser. - - -0.2.0 (2014-12-05) ------------------- - -* Major BC Break: method names for the Element interface have been renamed - from `serializeXml` and `deserializeXml` to `xmlSerialize` and - `xmlDeserialize`. This is so that it matches PHP's `JsonSerializable` - interface. -* #25: Added `XmlSerializable` to allow people to write serializers without - having to implement a deserializer in the same class. -* #26: Renamed the `Sabre\XML` namespace to `Sabre\Xml`. Due to composer magic - and the fact that PHP namespace are case-insensitive, this should not affect - anyone, unless you are doing exact string matches on class names. -* #23: It's not possible to automatically extract or serialize Xml fragments - from documents using `Sabre\Xml\Element\XmlFragment`. - - -0.1.0 (2014-11-24) ------------------- - -* #16: Added ability to override `elementMap`, `namespaceMap` and `baseUri` for - a fragment of a document during reading an writing using `pushContext` and - `popContext`. -* Removed: `Writer::$context` and `Reader::$context`. -* #15: Added `Reader::$baseUri` to match `Writer::$baseUri`. -* #20: Allow callbacks to be used instead of `Element` classes in the `Reader`. -* #25: Added `readText` to quickly grab all text from a node and advance the - reader to the next node. -* #15: Added `Sabre\XML\Element\Uri`. - - -0.0.6 (2014-09-26) ------------------- - -* Added: `CData` element. -* #13: Better support for xml with no namespaces. (@kalmas) -* Switched to PSR-4 directory structure. - - -0.0.5 (2013-03-27) ------------------- - -* Added: baseUri property to the Writer class. -* Added: The writeElement method can now write complex elements. -* Added: Throwing exception when invalid objects are written. - - -0.0.4 (2013-03-14) ------------------- - -* Fixed: The KeyValue parser was skipping over elements when there was no - whitespace between them. -* Fixed: Clearing libxml errors after parsing. -* Added: Support for CDATA. -* Added: Context properties. - - -0.0.3 (2013-02-22) ------------------- - -* Changed: Reader::parse returns an array with 1 level less depth. -* Added: A LibXMLException is now thrown if the XMLReader comes across an error. -* Fixed: Both the Elements and KeyValue parsers had severe issues with - nesting. -* Fixed: The reader now detects when the end of the document is hit before it - should (because we're still parsing an element). - - -0.0.2 (2013-02-17) ------------------- - -* Added: Elements parser. -* Added: KeyValue parser. -* Change: Reader::parseSubTree is now named parseInnerTree, and returns either - a string (in case of a text-node), or an array (in case there were child - elements). -* Added: Reader::parseCurrentElement is now public. - - -0.0.1 (2013-02-07) ------------------- - -* First alpha release - -Project started: 2012-11-13. First experiments in June 2009. diff --git a/htdocs/includes/sabre/sabre/xml/README.md b/htdocs/includes/sabre/sabre/xml/README.md index e6fc4db5f3d..55af24fc623 100644 --- a/htdocs/includes/sabre/sabre/xml/README.md +++ b/htdocs/includes/sabre/sabre/xml/README.md @@ -1,7 +1,7 @@ sabre/xml ========= -[![Build Status](https://secure.travis-ci.org/fruux/sabre-xml.svg?branch=master)](http://travis-ci.org/fruux/sabre-xml) +[![Build Status](https://secure.travis-ci.org/sabre-io/xml.svg?branch=master)](http://travis-ci.org/sabre-io/xml) The sabre/xml library is a specialized XML reader and writer. diff --git a/htdocs/includes/sabre/sabre/xml/composer.json b/htdocs/includes/sabre/sabre/xml/composer.json index 386f8213f5e..4524cf59bbb 100644 --- a/htdocs/includes/sabre/sabre/xml/composer.json +++ b/htdocs/includes/sabre/sabre/xml/composer.json @@ -5,7 +5,7 @@ "homepage" : "https://sabre.io/xml/", "license" : "BSD-3-Clause", "require" : { - "php" : ">=5.5.5", + "php" : "^7.1 || ^8.0", "ext-xmlwriter" : "*", "ext-xmlreader" : "*", "ext-dom" : "*", @@ -44,10 +44,24 @@ } }, "require-dev": { - "sabre/cs": "~1.0.0", - "phpunit/phpunit" : "*" + "friendsofphp/php-cs-fixer": "~2.17.1", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit" : "^7.5 || ^8.5 || ^9.0" }, - "config" : { - "bin-dir" : "bin/" + "scripts": { + "phpstan": [ + "phpstan analyse lib tests" + ], + "cs-fixer": [ + "php-cs-fixer fix" + ], + "phpunit": [ + "phpunit --configuration tests/phpunit.xml" + ], + "test": [ + "composer phpstan", + "composer cs-fixer", + "composer phpunit" + ] } } diff --git a/htdocs/includes/sabre/sabre/xml/lib/ContextStackTrait.php b/htdocs/includes/sabre/sabre/xml/lib/ContextStackTrait.php index ee3a3baca5c..4e15bd41424 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/ContextStackTrait.php +++ b/htdocs/includes/sabre/sabre/xml/lib/ContextStackTrait.php @@ -1,14 +1,16 @@ contextStack[] = [ $this->elementMap, $this->contextUri, $this->namespaceMap, - $this->classMap + $this->classMap, ]; - } /** * Restore the previous "context". - * - * @return null */ - function popContext() { - + public function popContext() + { list( $this->elementMap, $this->contextUri, $this->namespaceMap, $this->classMap ) = array_pop($this->contextStack); - } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Deserializer/functions.php b/htdocs/includes/sabre/sabre/xml/lib/Deserializer/functions.php index 2e5d877e912..c4f240970ec 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Deserializer/functions.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Deserializer/functions.php @@ -1,5 +1,7 @@ isEmptyElement) { $reader->next(); + + return []; + } + + if (!$reader->read()) { + $reader->next(); + + return []; + } + + if (Reader::END_ELEMENT === $reader->nodeType) { + $reader->next(); + return []; } $values = []; - $reader->read(); do { - - if ($reader->nodeType === Reader::ELEMENT) { - if ($namespace !== null && $reader->namespaceURI === $namespace) { + if (Reader::ELEMENT === $reader->nodeType) { + if (null !== $namespace && $reader->namespaceURI === $namespace) { $values[$reader->localName] = $reader->parseCurrentElement()['value']; } else { $clark = $reader->getClark(); $values[$clark] = $reader->parseCurrentElement()['value']; } } else { - $reader->read(); + if (!$reader->read()) { + break; + } } - } while ($reader->nodeType !== Reader::END_ELEMENT); + } while (Reader::END_ELEMENT !== $reader->nodeType); $reader->read(); return $values; - } /** @@ -133,37 +142,44 @@ function keyValue(Reader $reader, $namespace = null) { * "elem5", * ]; * - * @param Reader $reader - * @param string $namespace * @return string[] */ -function enum(Reader $reader, $namespace = null) { - +function enum(Reader $reader, string $namespace = null): array +{ // If there's no children, we don't do anything. if ($reader->isEmptyElement) { $reader->next(); + + return []; + } + if (!$reader->read()) { + $reader->next(); + + return []; + } + + if (Reader::END_ELEMENT === $reader->nodeType) { + $reader->next(); + return []; } - $reader->read(); $currentDepth = $reader->depth; $values = []; do { - - if ($reader->nodeType !== Reader::ELEMENT) { + if (Reader::ELEMENT !== $reader->nodeType) { continue; } if (!is_null($namespace) && $namespace === $reader->namespaceURI) { $values[] = $reader->localName; } else { - $values[] = $reader->getClark(); + $values[] = (string) $reader->getClark(); } - } while ($reader->depth >= $currentDepth && $reader->next()); $reader->next(); - return $values; + return $values; } /** @@ -173,16 +189,14 @@ function enum(Reader $reader, $namespace = null) { * This is primarily used by the mapValueObject function from the Service * class, but it can also easily be used for more specific situations. * - * @param Reader $reader - * @param string $className - * @param string $namespace * @return object */ -function valueObject(Reader $reader, $className, $namespace) { - +function valueObject(Reader $reader, string $className, string $namespace) +{ $valueObject = new $className(); if ($reader->isEmptyElement) { $reader->next(); + return $valueObject; } @@ -190,9 +204,7 @@ function valueObject(Reader $reader, $className, $namespace) { $reader->read(); do { - - if ($reader->nodeType === Reader::ELEMENT && $reader->namespaceURI == $namespace) { - + if (Reader::ELEMENT === $reader->nodeType && $reader->namespaceURI == $namespace) { if (property_exists($valueObject, $reader->localName)) { if (is_array($defaultProperties[$reader->localName])) { $valueObject->{$reader->localName}[] = $reader->parseCurrentElement()['value']; @@ -204,18 +216,20 @@ function valueObject(Reader $reader, $className, $namespace) { $reader->next(); } } else { - $reader->read(); + if (!$reader->read()) { + break; + } } - } while ($reader->nodeType !== Reader::END_ELEMENT); + } while (Reader::END_ELEMENT !== $reader->nodeType); $reader->read(); - return $valueObject; + return $valueObject; } /** * This deserializer helps you deserialize xml structures that look like - * this: + * this:. * * * ... @@ -223,7 +237,7 @@ function valueObject(Reader $reader, $className, $namespace) { * ... * * - * Many XML documents use patterns like that, and this deserializer + * Many XML documents use patterns like that, and this deserializer * allow you to get all the 'items' as an array. * * In that previous example, you would register the deserializer as such: @@ -234,25 +248,112 @@ function valueObject(Reader $reader, $className, $namespace) { * * The repeatingElements deserializer simply returns everything as an array. * - * @param Reader $reader - * @param string $childElementName Element name in clark-notation - * @return array + * $childElementName must either be a a clark-notation element name, or if no + * namespace is used, the bare element name. */ -function repeatingElements(Reader $reader, $childElementName) { - - if ($childElementName[0] !== '{') { - $childElementName = '{}' . $childElementName; +function repeatingElements(Reader $reader, string $childElementName): array +{ + if ('{' !== $childElementName[0]) { + $childElementName = '{}'.$childElementName; } $result = []; foreach ($reader->parseGetElements() as $element) { - if ($element['name'] === $childElementName) { $result[] = $element['value']; } - } return $result; - +} + +/** + * This deserializer helps you to deserialize structures which contain mixed content like this:. + * + *

      some text and a inline tagand even more text

      + * + * The above example will return + * + * [ + * 'some text', + * [ + * 'name' => '{}extref', + * 'value' => 'and a inline tag', + * 'attributes' => [] + * ], + * 'and even more text' + * ] + * + * In strict XML documents you wont find this kind of markup but in html this is a quite common pattern. + */ +function mixedContent(Reader $reader): array +{ + // If there's no children, we don't do anything. + if ($reader->isEmptyElement) { + $reader->next(); + + return []; + } + + $previousDepth = $reader->depth; + + $content = []; + $reader->read(); + while (true) { + if (Reader::ELEMENT == $reader->nodeType) { + $content[] = $reader->parseCurrentElement(); + } elseif ($reader->depth >= $previousDepth && in_array($reader->nodeType, [Reader::TEXT, Reader::CDATA, Reader::WHITESPACE])) { + $content[] = $reader->value; + $reader->read(); + } elseif (Reader::END_ELEMENT == $reader->nodeType) { + // Ensuring we are moving the cursor after the end element. + $reader->read(); + break; + } else { + $reader->read(); + } + } + + return $content; +} + +/** + * The functionCaller deserializer turns an xml element into whatever your callable return. + * + * You can use, e.g., a named constructor (factory method) to create an object using + * this function. + * + * @return mixed + */ +function functionCaller(Reader $reader, callable $func, string $namespace) +{ + if ($reader->isEmptyElement) { + $reader->next(); + + return null; + } + + $funcArgs = []; + $func = is_string($func) && false !== strpos($func, '::') ? explode('::', $func) : $func; + $ref = is_array($func) ? new \ReflectionMethod($func[0], $func[1]) : new \ReflectionFunction($func); + foreach ($ref->getParameters() as $parameter) { + $funcArgs[$parameter->getName()] = null; + } + + $reader->read(); + do { + if (Reader::ELEMENT === $reader->nodeType && $reader->namespaceURI == $namespace) { + if (array_key_exists($reader->localName, $funcArgs)) { + $funcArgs[$reader->localName] = $reader->parseCurrentElement()['value']; + } else { + // Ignore property + $reader->next(); + } + } else { + $reader->read(); + } + } while (Reader::END_ELEMENT !== $reader->nodeType); + $reader->read(); + + return $func(...array_values($funcArgs)); } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element.php b/htdocs/includes/sabre/sabre/xml/lib/Element.php index dd89c58882c..559eb54ea32 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Element.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Element.php @@ -1,5 +1,7 @@ value = $value; - } /** - * The xmlSerialize metod is called during xml writing. + * The xmlSerialize method is called during xml writing. * * Use the $writer argument to write its own xml serialization. * @@ -50,20 +49,16 @@ class Base implements Xml\Element { * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. - * - * @param Writer $writer - * @return void */ - function xmlSerialize(Xml\Writer $writer) { - + public function xmlSerialize(Xml\Writer $writer) + { $writer->write($this->value); - } /** * The deserialize method is called during xml parsing. * - * This method is called statictly, this is because in theory this method + * This method is called statically, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are @@ -78,14 +73,12 @@ class Base implements Xml\Element { * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * - * @param Xml\Reader $reader * @return mixed */ - static function xmlDeserialize(Xml\Reader $reader) { - + public static function xmlDeserialize(Xml\Reader $reader) + { $subTree = $reader->parseInnerTree(); + return $subTree; - } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/Cdata.php b/htdocs/includes/sabre/sabre/xml/lib/Element/Cdata.php index 5f42c4c6e8e..1367343ff17 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Element/Cdata.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/Cdata.php @@ -1,5 +1,7 @@ value = $value; } /** - * The xmlSerialize metod is called during xml writing. + * The xmlSerialize method is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element - * implementing XmlSerializble should only ever write what's considered + * implementing XmlSerializable should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a @@ -51,14 +51,9 @@ class Cdata implements Xml\XmlSerializable * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. - * - * @param Writer $writer - * @return void */ - function xmlSerialize(Xml\Writer $writer) { - + public function xmlSerialize(Xml\Writer $writer) + { $writer->writeCData($this->value); - } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/Elements.php b/htdocs/includes/sabre/sabre/xml/lib/Element/Elements.php index 9eefd1bf884..fecce4c7593 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Element/Elements.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/Elements.php @@ -1,5 +1,7 @@ * @@ -33,33 +35,30 @@ use Sabre\Xml\Serializer; * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ -class Elements implements Xml\Element { - +class Elements implements Xml\Element +{ /** - * Value to serialize + * Value to serialize. * * @var array */ protected $value; /** - * Constructor - * - * @param array $value + * Constructor. */ - function __construct(array $value = []) { - + public function __construct(array $value = []) + { $this->value = $value; - } /** - * The xmlSerialize metod is called during xml writing. + * The xmlSerialize method is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element - * implementing XmlSerializble should only ever write what's considered + * implementing XmlSerializable should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a @@ -68,20 +67,16 @@ class Elements implements Xml\Element { * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. - * - * @param Writer $writer - * @return void */ - function xmlSerialize(Xml\Writer $writer) { - + public function xmlSerialize(Xml\Writer $writer) + { Serializer\enum($writer, $this->value); - } /** * The deserialize method is called during xml parsing. * - * This method is called statictly, this is because in theory this method + * This method is called statically, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are @@ -96,13 +91,10 @@ class Elements implements Xml\Element { * $reader->parseSubTree() will parse the entire sub-tree, and advance to * the next element. * - * @param Xml\Reader $reader * @return mixed */ - static function xmlDeserialize(Xml\Reader $reader) { - + public static function xmlDeserialize(Xml\Reader $reader) + { return Deserializer\enum($reader); - } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/KeyValue.php b/htdocs/includes/sabre/sabre/xml/lib/Element/KeyValue.php index 7ce53bf4c6d..17448880dbf 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Element/KeyValue.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/KeyValue.php @@ -1,5 +1,7 @@ value = $value; - } /** - * The xmlSerialize metod is called during xml writing. + * The xmlSerialize method is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element - * implementing XmlSerializble should only ever write what's considered + * implementing XmlSerializable should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a @@ -68,20 +67,16 @@ class KeyValue implements Xml\Element { * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. - * - * @param Writer $writer - * @return void */ - function xmlSerialize(Xml\Writer $writer) { - + public function xmlSerialize(Xml\Writer $writer) + { $writer->write($this->value); - } /** * The deserialize method is called during xml parsing. * - * This method is called staticly, this is because in theory this method + * This method is called statically, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are @@ -96,13 +91,10 @@ class KeyValue implements Xml\Element { * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * - * @param Xml\Reader $reader * @return mixed */ - static function xmlDeserialize(Xml\Reader $reader) { - + public static function xmlDeserialize(Xml\Reader $reader) + { return Deserializer\keyValue($reader); - } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/Uri.php b/htdocs/includes/sabre/sabre/xml/lib/Element/Uri.php index 8f45c0027eb..336212a53d0 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Element/Uri.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/Uri.php @@ -1,5 +1,7 @@ value = $value; } /** - * The xmlSerialize metod is called during xml writing. + * The xmlSerialize method is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element - * implementing XmlSerializble should only ever write what's considered + * implementing XmlSerializable should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a @@ -54,19 +56,15 @@ class Uri implements Xml\Element { * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. - * - * @param Writer $writer - * @return void */ - function xmlSerialize(Xml\Writer $writer) { - + public function xmlSerialize(Xml\Writer $writer) + { $writer->text( \Sabre\Uri\resolve( $writer->contextUri, $this->value ) ); - } /** @@ -87,18 +85,15 @@ class Uri implements Xml\Element { * $reader->parseSubTree() will parse the entire sub-tree, and advance to * the next element. * - * @param Xml\Reader $reader * @return mixed */ - static function xmlDeserialize(Xml\Reader $reader) { - + public static function xmlDeserialize(Xml\Reader $reader) + { return new self( \Sabre\Uri\resolve( - $reader->contextUri, + (string) $reader->contextUri, $reader->readText() ) ); - } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Element/XmlFragment.php b/htdocs/includes/sabre/sabre/xml/lib/Element/XmlFragment.php index 642241ca485..bf110eaeefd 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Element/XmlFragment.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Element/XmlFragment.php @@ -1,5 +1,7 @@ xml = $xml; - - } - - function getXml() { - - return $this->xml; - } /** - * The xmlSerialize metod is called during xml writing. + * Returns the inner XML document. + */ + public function getXml(): string + { + return $this->xml; + } + + /** + * The xmlSerialize method is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element - * implementing XmlSerializble should only ever write what's considered + * implementing XmlSerializable should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a @@ -51,12 +62,9 @@ class XmlFragment implements Element { * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. - * - * @param Writer $writer - * @return void */ - function xmlSerialize(Writer $writer) { - + public function xmlSerialize(Writer $writer) + { $reader = new Reader(); // Wrapping the xml in a container, so root-less values can still be @@ -69,29 +77,27 @@ XML; $reader->xml($xml); while ($reader->read()) { - if ($reader->depth < 1) { // Skipping the root node. continue; } switch ($reader->nodeType) { - - case Reader::ELEMENT : + case Reader::ELEMENT: $writer->startElement( - $reader->getClark() + (string) $reader->getClark() ); $empty = $reader->isEmptyElement; while ($reader->moveToNextAttribute()) { switch ($reader->namespaceURI) { - case '' : + case '': $writer->writeAttribute($reader->localName, $reader->value); break; - case 'http://www.w3.org/2000/xmlns/' : + case 'http://www.w3.org/2000/xmlns/': // Skip namespace declarations break; - default : - $writer->writeAttribute($reader->getClark(), $reader->value); + default: + $writer->writeAttribute((string) $reader->getClark(), $reader->value); break; } } @@ -99,26 +105,23 @@ XML; $writer->endElement(); } break; - case Reader::CDATA : - case Reader::TEXT : + case Reader::CDATA: + case Reader::TEXT: $writer->text( $reader->value ); break; - case Reader::END_ELEMENT : + case Reader::END_ELEMENT: $writer->endElement(); break; - } - } - } /** * The deserialize method is called during xml parsing. * - * This method is called statictly, this is because in theory this method + * This method is called statically, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are @@ -133,15 +136,13 @@ XML; * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * - * @param Reader $reader * @return mixed */ - static function xmlDeserialize(Reader $reader) { - + public static function xmlDeserialize(Reader $reader) + { $result = new self($reader->readInnerXml()); $reader->next(); + return $result; - } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/LibXMLException.php b/htdocs/includes/sabre/sabre/xml/lib/LibXMLException.php index f0190eb51ea..fb074f97da9 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/LibXMLException.php +++ b/htdocs/includes/sabre/sabre/xml/lib/LibXMLException.php @@ -1,12 +1,14 @@ errors = $errors; - parent::__construct($errors[0]->message . ' on line ' . $errors[0]->line . ', column ' . $errors[0]->column, $code, $previousException); - + parent::__construct($errors[0]->message.' on line '.$errors[0]->line.', column '.$errors[0]->column, $code, $previousException); } /** - * Returns the LibXML errors - * - * @return void + * Returns the LibXML errors. */ - function getErrors() { - + public function getErrors(): array + { return $this->errors; - } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/ParseException.php b/htdocs/includes/sabre/sabre/xml/lib/ParseException.php index 3a6883b2ff8..e237b8732b4 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/ParseException.php +++ b/htdocs/includes/sabre/sabre/xml/lib/ParseException.php @@ -1,9 +1,10 @@ localName) { + public function getClark() + { + if (!$this->localName) { return null; } - return '{' . $this->namespaceURI . '}' . $this->localName; - + return '{'.$this->namespaceURI.'}'.$this->localName; } /** @@ -51,65 +52,65 @@ class Reader extends XMLReader { * * This function will also disable the standard libxml error handler (which * usually just results in PHP errors), and throw exceptions instead. - * - * @return array */ - function parse() { - - $previousEntityState = libxml_disable_entity_loader(true); + public function parse(): array + { + $previousEntityState = null; + $shouldCallLibxmlDisableEntityLoader = (\LIBXML_VERSION < 20900); + if ($shouldCallLibxmlDisableEntityLoader) { + $previousEntityState = libxml_disable_entity_loader(true); + } $previousSetting = libxml_use_internal_errors(true); try { - - // Really sorry about the silence operator, seems like I have no - // choice. See: - // - // https://bugs.php.net/bug.php?id=64230 - while ($this->nodeType !== self::ELEMENT && @$this->read()) { - // noop + while (self::ELEMENT !== $this->nodeType) { + if (!$this->read()) { + $errors = libxml_get_errors(); + libxml_clear_errors(); + if ($errors) { + throw new LibXMLException($errors); + } + } } $result = $this->parseCurrentElement(); + // last line of defense in case errors did occur above $errors = libxml_get_errors(); libxml_clear_errors(); if ($errors) { throw new LibXMLException($errors); } - } finally { libxml_use_internal_errors($previousSetting); - libxml_disable_entity_loader($previousEntityState); + if ($shouldCallLibxmlDisableEntityLoader) { + libxml_disable_entity_loader($previousEntityState); + } } return $result; } - - /** * parseGetElements parses everything in the current sub-tree, - * and returns a an array of elements. + * and returns an array of elements. * * Each element has a 'name', 'value' and 'attributes' key. * - * If the the element didn't contain sub-elements, an empty array is always + * If the element didn't contain sub-elements, an empty array is always * returned. If there was any text inside the element, it will be * discarded. * * If the $elementMap argument is specified, the existing elementMap will * be overridden while parsing the tree, and restored after this process. - * - * @param array $elementMap - * @return array */ - function parseGetElements(array $elementMap = null) { - + public function parseGetElements(array $elementMap = null): array + { $result = $this->parseInnerTree($elementMap); if (!is_array($result)) { return []; } - return $result; + return $result; } /** @@ -123,17 +124,17 @@ class Reader extends XMLReader { * If the $elementMap argument is specified, the existing elementMap will * be overridden while parsing the tree, and restored after this process. * - * @param array $elementMap - * @return array|string + * @return array|string|null */ - function parseInnerTree(array $elementMap = null) { - + public function parseInnerTree(array $elementMap = null) + { $text = null; $elements = []; - if ($this->nodeType === self::ELEMENT && $this->isEmptyElement) { + if (self::ELEMENT === $this->nodeType && $this->isEmptyElement) { // Easy! $this->next(); + return null; } @@ -143,12 +144,7 @@ class Reader extends XMLReader { } try { - - // Really sorry about the silence operator, seems like I have no - // choice. See: - // - // https://bugs.php.net/bug.php?id=64230 - if (!@$this->read()) { + if (!$this->read()) { $errors = libxml_get_errors(); libxml_clear_errors(); if ($errors) { @@ -157,10 +153,10 @@ class Reader extends XMLReader { throw new ParseException('This should never happen (famous last words)'); } - while (true) { + $keepOnParsing = true; + while ($keepOnParsing) { if (!$this->isValid()) { - $errors = libxml_get_errors(); if ($errors) { @@ -170,46 +166,41 @@ class Reader extends XMLReader { } switch ($this->nodeType) { - case self::ELEMENT : + case self::ELEMENT: $elements[] = $this->parseCurrentElement(); break; - case self::TEXT : - case self::CDATA : + case self::TEXT: + case self::CDATA: $text .= $this->value; $this->read(); break; - case self::END_ELEMENT : + case self::END_ELEMENT: // Ensuring we are moving the cursor after the end element. $this->read(); - break 2; - case self::NONE : + $keepOnParsing = false; + break; + case self::NONE: throw new ParseException('We hit the end of the document prematurely. This likely means that some parser "eats" too many elements. Do not attempt to continue parsing.'); - default : + default: // Advance to the next element $this->read(); break; } - } - } finally { - if (!is_null($elementMap)) { $this->popContext(); } - } - return ($elements ? $elements : $text); + return $elements ? $elements : $text; } /** * Reads all text below the current element, and returns this as a string. - * - * @return string */ - function readText() { - + public function readText(): string + { $result = ''; $previousDepth = $this->depth; @@ -218,8 +209,8 @@ class Reader extends XMLReader { $result .= $this->value; } } - return $result; + return $result; } /** @@ -229,11 +220,9 @@ class Reader extends XMLReader { * * name - A clark-notation XML element name. * * value - The parsed value. * * attributes - A key-value list of attributes. - * - * @return array */ - function parseCurrentElement() { - + public function parseCurrentElement(): array + { $name = $this->getClark(); $attributes = []; @@ -243,43 +232,38 @@ class Reader extends XMLReader { } $value = call_user_func( - $this->getDeserializerForElementName($name), + $this->getDeserializerForElementName((string) $name), $this ); return [ - 'name' => $name, - 'value' => $value, + 'name' => $name, + 'value' => $value, 'attributes' => $attributes, ]; } - /** * Grabs all the attributes from the current element, and returns them as a * key-value array. * * If the attributes are part of the same namespace, they will simply be * short keys. If they are defined on a different namespace, the attribute - * name will be retured in clark-notation. - * - * @return array + * name will be returned in clark-notation. */ - function parseAttributes() { - + public function parseAttributes(): array + { $attributes = []; while ($this->moveToNextAttribute()) { if ($this->namespaceURI) { - // Ignoring 'xmlns', it doesn't make any sense. - if ($this->namespaceURI === 'http://www.w3.org/2000/xmlns/') { + if ('http://www.w3.org/2000/xmlns/' === $this->namespaceURI) { continue; } $name = $this->getClark(); $attributes[$name] = $this->value; - } else { $attributes[$this->localName] = $this->value; } @@ -287,21 +271,16 @@ class Reader extends XMLReader { $this->moveToElement(); return $attributes; - } /** * Returns the function that should be used to parse the element identified - * by it's clark-notation name. - * - * @param string $name - * @return callable + * by its clark-notation name. */ - function getDeserializerForElementName($name) { - - + public function getDeserializerForElementName(string $name): callable + { if (!array_key_exists($name, $this->elementMap)) { - if (substr($name, 0, 2) == '{}' && array_key_exists(substr($name, 2), $this->elementMap)) { + if ('{}' == substr($name, 0, 2) && array_key_exists(substr($name, 2), $this->elementMap)) { $name = substr($name, 2); } else { return ['Sabre\\Xml\\Element\\Base', 'xmlDeserialize']; @@ -318,13 +297,11 @@ class Reader extends XMLReader { } $type = gettype($deserializer); - if ($type === 'string') { - $type .= ' (' . $deserializer . ')'; - } elseif ($type === 'object') { - $type .= ' (' . get_class($deserializer) . ')'; + if ('string' === $type) { + $type .= ' ('.$deserializer.')'; + } elseif ('object' === $type) { + $type .= ' ('.get_class($deserializer).')'; } - throw new \LogicException('Could not use this type as a deserializer: ' . $type . ' for element: ' . $name); - + throw new \LogicException('Could not use this type as a deserializer: '.$type.' for element: '.$name); } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Serializer/functions.php b/htdocs/includes/sabre/sabre/xml/lib/Serializer/functions.php index 21448017d94..8d0330558cd 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Serializer/functions.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Serializer/functions.php @@ -1,5 +1,7 @@ content * * - * @param Writer $writer * @param string[] $values - * @return void */ -function enum(Writer $writer, array $values) { - +function enum(Writer $writer, array $values) +{ foreach ($values as $value) { $writer->writeElement($value); } @@ -54,29 +54,26 @@ function enum(Writer $writer, array $values) { * Values that are set to null or an empty array are not serialized. To * serialize empty properties, you must specify them as an empty string. * - * @param Writer $writer * @param object $valueObject - * @param string $namespace */ -function valueObject(Writer $writer, $valueObject, $namespace) { +function valueObject(Writer $writer, $valueObject, string $namespace) +{ foreach (get_object_vars($valueObject) as $key => $val) { if (is_array($val)) { // If $val is an array, it has a special meaning. We need to // generate one child element for each item in $val foreach ($val as $child) { - $writer->writeElement('{' . $namespace . '}' . $key, $child); + $writer->writeElement('{'.$namespace.'}'.$key, $child); } - - } elseif ($val !== null) { - $writer->writeElement('{' . $namespace . '}' . $key, $val); + } elseif (null !== $val) { + $writer->writeElement('{'.$namespace.'}'.$key, $val); } } } - /** * This serializer helps you serialize xml structures that look like - * this: + * this:. * * * ... @@ -88,18 +85,12 @@ function valueObject(Writer $writer, $valueObject, $namespace) { * and this could be called like this: * * repeatingElements($writer, $items, '{}item'); - * - * @param Writer $writer - * @param array $items A list of items sabre/xml can serialize. - * @param string $childElementName Element name in clark-notation - * @return void */ -function repeatingElements(Writer $writer, array $items, $childElementName) { - +function repeatingElements(Writer $writer, array $items, string $childElementName) +{ foreach ($items as $item) { $writer->writeElement($childElementName, $item); } - } /** @@ -157,38 +148,23 @@ function repeatingElements(Writer $writer, array $items, $childElementName) { * * You can even mix the two array syntaxes. * - * @param Writer $writer - * @param string|int|float|bool|array|object - * @return void + * @param string|int|float|bool|array|object $value */ -function standardSerializer(Writer $writer, $value) { - +function standardSerializer(Writer $writer, $value) +{ if (is_scalar($value)) { - // String, integer, float, boolean - $writer->text($value); - + $writer->text((string) $value); } elseif ($value instanceof XmlSerializable) { - // XmlSerializable classes or Element classes. $value->xmlSerialize($writer); - } elseif (is_object($value) && isset($writer->classMap[get_class($value)])) { - // It's an object which class appears in the classmap. $writer->classMap[get_class($value)]($writer, $value); - } elseif (is_callable($value)) { - // A callback $value($writer); - - } elseif (is_null($value)) { - - // nothing! - } elseif (is_array($value) && array_key_exists('name', $value)) { - // if the array had a 'name' element, we assume that this array // describes a 'name' and optionally 'attributes' and 'value'. @@ -200,19 +176,13 @@ function standardSerializer(Writer $writer, $value) { $writer->writeAttributes($attributes); $writer->write($value); $writer->endElement(); - } elseif (is_array($value)) { - foreach ($value as $name => $item) { - if (is_int($name)) { - // This item has a numeric index. We just loop through the // array and throw it back in the writer. standardSerializer($writer, $item); - } elseif (is_string($name) && is_array($item) && isset($item['attributes'])) { - // The key is used for a name, but $item has 'attributes' and // possibly 'value' $writer->startElement($name); @@ -221,29 +191,18 @@ function standardSerializer(Writer $writer, $value) { $writer->write($item['value']); } $writer->endElement(); - } elseif (is_string($name)) { - // This was a plain key-value array. $writer->startElement($name); $writer->write($item); $writer->endElement(); - } else { - - throw new InvalidArgumentException('The writer does not know how to serialize arrays with keys of type: ' . gettype($name)); - + throw new InvalidArgumentException('The writer does not know how to serialize arrays with keys of type: '.gettype($name)); } } - } elseif (is_object($value)) { - - throw new InvalidArgumentException('The writer cannot serialize objects of class: ' . get_class($value)); - - } else { - - throw new InvalidArgumentException('The writer cannot serialize values of type: ' . gettype($value)); - + throw new InvalidArgumentException('The writer cannot serialize objects of class: '.get_class($value)); + } elseif (!is_null($value)) { + throw new InvalidArgumentException('The writer cannot serialize values of type: '.gettype($value)); } - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Service.php b/htdocs/includes/sabre/sabre/xml/lib/Service.php index 09ee341cf8f..a8e34d254c9 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Service.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Service.php @@ -1,11 +1,13 @@ elementMap = $this->elementMap; - return $r; + return $r; } /** - * Returns a fresh xml writer - * - * @return Writer + * Returns a fresh xml writer. */ - function getWriter() { - + public function getWriter(): Writer + { $w = new Writer(); $w->namespaceMap = $this->namespaceMap; $w->classMap = $this->classMap; - return $w; + return $w; } /** @@ -99,26 +104,32 @@ class Service { * with the root element name of the document. * * @param string|resource $input - * @param string|null $contextUri - * @param string|null $rootElementName + * * @throws ParseException + * * @return array|object|string */ - function parse($input, $contextUri = null, &$rootElementName = null) { - + public function parse($input, string $contextUri = null, string &$rootElementName = null) + { if (is_resource($input)) { // Unfortunately the XMLReader doesn't support streams. When it // does, we can optimize this. - $input = stream_get_contents($input); + $input = (string) stream_get_contents($input); } + + // If input is empty, then it's safe to throw an exception + if (empty($input)) { + throw new ParseException('The input element to parse is empty. Do not attempt to parse'); + } + $r = $this->getReader(); $r->contextUri = $contextUri; - $r->xml($input); + $r->XML($input, null, $this->options); $result = $r->parse(); $rootElementName = $result['name']; - return $result['value']; + return $result['value']; } /** @@ -137,32 +148,42 @@ class Service { * * @param string|string[] $rootElementName * @param string|resource $input - * @param string|null $contextUri - * @return void + * + * @throws ParseException + * + * @return array|object|string */ - function expect($rootElementName, $input, $contextUri = null) { - + public function expect($rootElementName, $input, string $contextUri = null) + { if (is_resource($input)) { // Unfortunately the XMLReader doesn't support streams. When it // does, we can optimize this. - $input = stream_get_contents($input); + $input = (string) stream_get_contents($input); } + + // If input is empty, then it's safe to throw an exception + if (empty($input)) { + throw new ParseException('The input element to parse is empty. Do not attempt to parse'); + } + $r = $this->getReader(); $r->contextUri = $contextUri; - $r->xml($input); + $r->XML($input, null, $this->options); - $rootElementName = (array)$rootElementName; + $rootElementName = (array) $rootElementName; foreach ($rootElementName as &$rEl) { - if ($rEl[0] !== '{') $rEl = '{}' . $rEl; + if ('{' !== $rEl[0]) { + $rEl = '{}'.$rEl; + } } $result = $r->parse(); if (!in_array($result['name'], $rootElementName, true)) { - throw new ParseException('Expected ' . implode(' or ', (array)$rootElementName) . ' but received ' . $result['name'] . ' as the root element'); + throw new ParseException('Expected '.implode(' or ', $rootElementName).' but received '.$result['name'].' as the root element'); } - return $result['value']; + return $result['value']; } /** @@ -179,26 +200,26 @@ class Service { * This allows an implementor to easily create URI's relative to the root * of the domain. * - * @param string $rootElementName - * @param string|array|XmlSerializable $value - * @param string|null $contextUri + * @param string|array|object|XmlSerializable $value + * + * @return string */ - function write($rootElementName, $value, $contextUri = null) { - + public function write(string $rootElementName, $value, string $contextUri = null) + { $w = $this->getWriter(); $w->openMemory(); $w->contextUri = $contextUri; $w->setIndent(true); $w->startDocument(); $w->writeElement($rootElementName, $value); - return $w->outputMemory(); + return $w->outputMemory(); } /** - * Map an xml element to a PHP class. + * Map an XML element to a PHP class. * - * Calling this function will automatically setup the Reader and Writer + * Calling this function will automatically set up the Reader and Writer * classes to turn a specific XML element to a PHP class. * * For example, given a class such as : @@ -218,18 +239,15 @@ class Service { * These can easily be mapped by calling: * * $service->mapValueObject('{http://example.org}author', 'Author'); - * - * @param string $elementName - * @param object $className - * @return void */ - function mapValueObject($elementName, $className) { + public function mapValueObject(string $elementName, string $className) + { list($namespace) = self::parseClarkNotation($elementName); - $this->elementMap[$elementName] = function(Reader $reader) use ($className, $namespace) { + $this->elementMap[$elementName] = function (Reader $reader) use ($className, $namespace) { return \Sabre\Xml\Deserializer\valueObject($reader, $className, $namespace); }; - $this->classMap[$className] = function(Writer $writer, $valueObject) use ($namespace) { + $this->classMap[$className] = function (Writer $writer, $valueObject) use ($namespace) { return \Sabre\Xml\Serializer\valueObject($writer, $valueObject, $namespace); }; $this->valueObjectMap[$className] = $elementName; @@ -245,20 +263,20 @@ class Service { * mapValueObject(). * * @param object $object - * @param string $contextUri - * @return void + * + * @throws \InvalidArgumentException */ - function writeValueObject($object, $contextUri = null) { - + public function writeValueObject($object, string $contextUri = null) + { if (!isset($this->valueObjectMap[get_class($object)])) { - throw new \InvalidArgumentException('"' . get_class($object) . '" is not a registered value object class. Register your class with mapValueObject.'); + throw new \InvalidArgumentException('"'.get_class($object).'" is not a registered value object class. Register your class with mapValueObject.'); } + return $this->write( $this->valueObjectMap[get_class($object)], $object, $contextUri ); - } /** @@ -267,22 +285,20 @@ class Service { * * If the string was invalid, it will throw an InvalidArgumentException. * - * @param string $str - * @throws InvalidArgumentException - * @return array + * @throws \InvalidArgumentException */ - static function parseClarkNotation($str) { + public static function parseClarkNotation(string $str): array + { static $cache = []; if (!isset($cache[$str])) { - if (!preg_match('/^{([^}]*)}(.*)$/', $str, $matches)) { - throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); + throw new \InvalidArgumentException('\''.$str.'\' is not a valid clark-notation formatted string'); } $cache[$str] = [ $matches[1], - $matches[2] + $matches[2], ]; } @@ -293,5 +309,4 @@ class Service { * A list of classes and which XML elements they map to. */ protected $valueObjectMap = []; - } diff --git a/htdocs/includes/sabre/sabre/xml/lib/Version.php b/htdocs/includes/sabre/sabre/xml/lib/Version.php index 7edb40d67ac..1144bf08545 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/Version.php +++ b/htdocs/includes/sabre/sabre/xml/lib/Version.php @@ -1,5 +1,7 @@ * + * Note: this function doesn't have the string typehint, because PHP's + * XMLWriter::startElement doesn't either. + * * @param string $name - * @return bool */ - function startElement($name) { - - if ($name[0] === '{') { - + public function startElement($name): bool + { + if ('{' === $name[0]) { list($namespace, $localName) = Service::parseClarkNotation($name); if (array_key_exists($namespace, $this->namespaceMap)) { $result = $this->startElementNS( - $this->namespaceMap[$namespace] === '' ? null : $this->namespaceMap[$namespace], + '' === $this->namespaceMap[$namespace] ? null : $this->namespaceMap[$namespace], $localName, null ); } else { - // An empty namespace means it's the global namespace. This is // allowed, but it mustn't get a prefix. - if ($namespace === "" || $namespace === null) { + if ('' === $namespace || null === $namespace) { $result = $this->startElement($localName); $this->writeAttribute('xmlns', ''); } else { if (!isset($this->adhocNamespaces[$namespace])) { - $this->adhocNamespaces[$namespace] = 'x' . (count($this->adhocNamespaces) + 1); + $this->adhocNamespaces[$namespace] = 'x'.(count($this->adhocNamespaces) + 1); } $result = $this->startElementNS($this->adhocNamespaces[$namespace], $localName, $namespace); } } - } else { $result = parent::startElement($name); } if (!$this->namespacesWritten) { - foreach ($this->namespaceMap as $namespace => $prefix) { - $this->writeAttribute(($prefix ? 'xmlns:' . $prefix : 'xmlns'), $namespace); + $this->writeAttribute(($prefix ? 'xmlns:'.$prefix : 'xmlns'), $namespace); } $this->namespacesWritten = true; - } return $result; - } /** @@ -182,18 +178,20 @@ class Writer extends XMLWriter { * becomes: * Evert Pot * - * @param string $name - * @param string $content - * @return bool + * Note: this function doesn't have the string typehint, because PHP's + * XMLWriter::startElement doesn't either. + * + * @param array|string|object|null $content */ - function writeElement($name, $content = null) { - + public function writeElement($name, $content = null): bool + { $this->startElement($name); if (!is_null($content)) { $this->write($content); } $this->endElement(); + return true; } /** @@ -204,16 +202,12 @@ class Writer extends XMLWriter { * The key is an attribute name. If the key is a 'localName', the current * xml namespace is assumed. If it's a 'clark notation key', this namespace * will be used instead. - * - * @param array $attributes - * @return void */ - function writeAttributes(array $attributes) { - + public function writeAttributes(array $attributes) + { foreach ($attributes as $name => $value) { $this->writeAttribute($name, $value); } - } /** @@ -223,44 +217,41 @@ class Writer extends XMLWriter { * * Returns true when successful. * + * Note: this function doesn't have typehints, because for some reason + * PHP's XMLWriter::writeAttribute doesn't either. + * * @param string $name * @param string $value - * @return bool */ - function writeAttribute($name, $value) { - - if ($name[0] === '{') { - - list( - $namespace, - $localName - ) = Service::parseClarkNotation($name); - - if (array_key_exists($namespace, $this->namespaceMap)) { - // It's an attribute with a namespace we know - $this->writeAttribute( - $this->namespaceMap[$namespace] . ':' . $localName, - $value - ); - } else { - - // We don't know the namespace, we must add it in-line - if (!isset($this->adhocNamespaces[$namespace])) { - $this->adhocNamespaces[$namespace] = 'x' . (count($this->adhocNamespaces) + 1); - } - $this->writeAttributeNS( - $this->adhocNamespaces[$namespace], - $localName, - $namespace, - $value - ); - - } - - } else { + public function writeAttribute($name, $value): bool + { + if ('{' !== $name[0]) { return parent::writeAttribute($name, $value); } - } + list( + $namespace, + $localName + ) = Service::parseClarkNotation($name); + if (array_key_exists($namespace, $this->namespaceMap)) { + // It's an attribute with a namespace we know + return $this->writeAttribute( + $this->namespaceMap[$namespace].':'.$localName, + $value + ); + } + + // We don't know the namespace, we must add it in-line + if (!isset($this->adhocNamespaces[$namespace])) { + $this->adhocNamespaces[$namespace] = 'x'.(count($this->adhocNamespaces) + 1); + } + + return $this->writeAttributeNS( + $this->adhocNamespaces[$namespace], + $localName, + $namespace, + $value + ); + } } diff --git a/htdocs/includes/sabre/sabre/xml/lib/XmlDeserializable.php b/htdocs/includes/sabre/sabre/xml/lib/XmlDeserializable.php index fa857e82c79..83f33db1e7e 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/XmlDeserializable.php +++ b/htdocs/includes/sabre/sabre/xml/lib/XmlDeserializable.php @@ -1,5 +1,7 @@ parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * - * @param Reader $reader * @return mixed */ - static function xmlDeserialize(Reader $reader); - + public static function xmlDeserialize(Reader $reader); } diff --git a/htdocs/includes/sabre/sabre/xml/lib/XmlSerializable.php b/htdocs/includes/sabre/sabre/xml/lib/XmlSerializable.php index 3e2c528b945..2affc33f15c 100644 --- a/htdocs/includes/sabre/sabre/xml/lib/XmlSerializable.php +++ b/htdocs/includes/sabre/sabre/xml/lib/XmlSerializable.php @@ -1,5 +1,7 @@